std::bind,std::mem_fn,std::invoke
一、std::bind
std::bind会把所有参数都拷贝,即使它是个左值引用。std::thread就是最好的例子,std::ref也是为了std::bind而引入的
std::bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在对象的成员函数前添加&
std::bind 和 std::placeholders
placeholders是占位符。表示新的函数对象(std::bind返回的东西)中参数的位置(_2:表示第二个实参)。当调用新的函数对象时,新函数对象会调用被调用函数,并且其参数会传递到被调用函数参数列表中持有与新函数对象中位置对应的占位符。
placeholders是一个命名空间,其本身定义在std命名空间中。placeholder中有名字_n (1,2,3,4,……n)。为了使用这些名字,两个命名空间都必须写上。例如:using namespace std::placeholders::_1;
示例
void fun(arg1, arg2, arg3, arg4, arg5)
{
//do something
}
auto g = bind(fun, a, b, _2, c, _1);
// g(x,y) = fun(a,b,y,c,x)
转载:标准库bind函数中使用占位符placeholders
点击查看代码
#include <iostream>
#include <functional>
#include <string>
using namespace std;
int TestFunc(int a, string c, float f)
{
cout << a << endl;
cout << c << endl;
cout << f << endl;
return a;
}
int main()
{
auto bindFunc1 = bind(TestFunc, std::placeholders::_1, "abc", 66.66);
bindFunc1(10);
cout << "================================\n";
bindFunc1(6.6, "sss", "ggg"); //实参个数可以多于placeholders的个数,返回值为auto才可以这样
//返回值为:function<int(int, string, float)> 则实参必须为:int,string,float且参数个数必须为3
cout << "=================================\n";
auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 77.77);
bindFunc2("xxx", 10);
//bindFunc2("yyy"); //错误,实参个数不能少于placeholders个数,相同的placeholder算一个
cout << "=================================\n";
auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, std::placeholders::_2);
bindFunc3("ss", 2); //只有两个placeholders::_1,_2
cout << "=================================\n";
auto bindFunc4 = bind(TestFunc, std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);
//bind相当于TestFunc(_3,_2,_1); _3表示bindFunc4的实参中的第三个参数。
bindFunc4(5.5, "hhh", 2); //实参类型根据bind中placeholders确定
return 0;
}
二、std::mem_fn
std::mem_fn比 std::bind功能更简单,std::bind功能更复杂,如果使用mem_fn能解决问题就不要用std::bind
参数必须为类成员变量或函数,简单理解就是利用一个成员函数生成一个函数对象
示例代码
点击查看代码
#include <functional>
#include <iostream>
class Foo
{
public:
void no_arg()
{
std::cout << "Hello, world.\n";
}
void has_arg(int i)
{
std::cout << "number: " << i << '\n';
}
int data = 7;
};
int main()
{
Foo f;
// 参数必须为类成员变量或函数
auto fn1 = std::mem_fn(&Foo::no_arg);
fn1(&f);
auto b1 = std::bind(&Foo::no_arg, f);
b1();
auto fn2 = std::mem_fn(&Foo::has_arg);
fn2(&f, 42);
auto b2 = std::bind(&Foo::has_arg, f, 3);
b2();
auto fn3 = std::mem_fn(&Foo::data);
std::cout << "data: " << fn3(&f) << '\n';
return 0;
}
三、std::invoke
functional仿函数,std::bind,std::invoke
invoke简单理解就是用来调用函数的(普通函数,成员函数,访问数据成员,lambda,函数对象都可以),可以完美替代#define宏
为什么使用std::invoke
点击查看代码
#include <iostream>
#define WARP_CALL(fun, ...) fun(__VA_ARGS__)
template <typename Fun,typename...Args>
auto warp_call1(Fun f, Args... args)->decltype(f(args...))
{
return f(args...); //注意此处args后面的...不要忘记
}
template <typename Fun,typename...Args>
auto warp_call2(Fun&& f, Args&&...args)
{
// 只是给f和args多了一步完美转发,注意...的位置
return std::forward<Fun>(f)(std::forward<Args>(args)...);
}
template<typename Fun,typename...Args>
decltype(auto) warp_call3(Fun&& f, Args&&... args)noexcept
{
return std::forward<Fun>(f)(std::forward<Args>(args)...);
}
template<typename Fun,typename...Args>
constexpr auto warp_call4(Fun&& f, Args&&... args) noexcept
->decltype(std::forward<Fun>(f)(std::forward<Args>(args)...))
{
return std::forward<Fun>(f)(std::forward<Args>(args)...);
}
int fun(int x,int y)
{
return x + y;
}
int main()
{
auto ret1 = WARP_CALL(fun, 2, 2);
std::cout << "x + y = " << ret1 << std::endl;
auto ret2 = warp_call1(fun, 2, 4);
std::cout << "x + y = " << ret2 << std::endl;
auto ret3 = warp_call2(fun, 1, 4);
std::cout << "x + y = " << ret3 << std::endl;
auto ret4 = warp_call3(fun, 4, 4);
std::cout << "x + y = " << ret4 << std::endl;
//std::invoke就相当于warp_call4(当然invoke更好用)
auto ret5 = warp_call4(fun, 3, 4);
std::cout << "x + y = " << ret5 << std::endl;
return 0;
}
std::invoke使用示例代码
点击查看代码
#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_;
};
int print_num(int i)
{
std::cout << i << '\n';
return 2;
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// 调用自由函数
auto ret = std::invoke(print_num, -9);
// 调用 lambda
std::invoke([]() { print_num(42); });
// 调用成员函数
const Foo foo(314159);
std::invoke(&Foo::print_add, foo, 1);
// 调用(访问)数据成员
std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n';
// 调用函数对象
std::invoke(PrintNum(), 18);
return 0;
}
std::invoke_result用来获取调用函数的返回值类型。
#include <type_traits>
#include <iostream>
typedef int(FUN)(char,int,float);
int main()
{
std::invoke_result_t<FUN,double,float,int> s; //int s;
std::invoke_result<FUN, int, int, int>::type t; //int t;
t = 3;
s = 2;
return 0;
}