C++ Weekly - Episode 17 脱水版: C++17's std::invoke
这一期主要讲了一个调用模板方法 std::invoke
(C++17, C++20, C++23)。
cppreference: en.cppreference.com/w/cpp/utili…
从 C++ 17 开始, 标准库提供 std::invoke
可以调用普通函数, 成员函数, 函数指针, 还有 Lambda, 可调用对象等. 由于 cppreference 解释和例子复杂难懂, John 通过以下代码举了例子. 似乎更新了例子, 也很清晰, 这里作摘录 (怀疑官网的例子是不是 John 去更新的, 现在内容基本一致).
可以看到, std::invoke
各种可调用对象都实现了比较统一的调用方式, 甚至对于普通类成员可以.
John:
int do_something(const int i)
{
return 5 + i;
}
struct S
{
int j = 5;
int do_something(const int i) {
return j + i;
}
int do_something_2(const int i) {
return j * i;
}
};
int main()
{
std::cout << std::invoke(&do_something, 5) << "\n";
S s;
std::cout << s.do_something(3) << '\n';
auto fp = &S::do_something;
int (S::*fp2)(int) = &S::do_something;
int (S::*fp3)(int) = nullptr;
if (true) {
fp3 = &S::do_something;
} else {
fp3 = &S::do_something_2;
}
std::cout << (s.*fp)(2) << '\n';
std::cout << (s.*fp2)(1) << '\n';
//
std::cout << std::invoke(&S::do_something, s, 10) << '\n';
std::cout << std::invoke(&S::do_something_2, s, 10) << '\n';
std::cout << std::invoke(fp2, s, 10) << '\n';
std::cout << std::invoke(&S::j, s) << '\n';
}
cppreference:
#include <functional>
#include <iostream>
#include <type_traits>
struct Foo
{
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << '\n';
}
struct PrintNum
{
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// invoke a free function
std::invoke(print_num, -9);
// invoke a lambda
std::invoke([]() { print_num(42); });
// invoke a member function
const Foo foo(314159);
std::invoke(&Foo::print_add, foo, 1);
// invoke (access) a data member
std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n';
// invoke a function object
std::invoke(PrintNum(), 18);
# if defined(__cpp_lib_invoke_r)
auto add = [](int x, int y) { return x + y; };
auto ret = std::invoke_r<float>(add, 11, 22);
static_assert(std::is_same<decltype(ret), float>());
std::cout << ret << '\n';
std::invoke_r<void>(print_num, 44);
# endif
}