先看如下代码:
double my_divide (double x, double y) {return x/y;}class A {
public:void fun_3(int k,int m) {std::cout << "print: k = "<< k << ", m = " << m << std::endl;}
};
int main()
{A a;//f5的类型为 function<void(int, int)>auto f5 = std::bind(&A::fun_3, &a, std::placeholders::_1, std::placeholders::_2); //使用auto关键字f5(10, 20); //调用a.fun_3(10,20),print: k=10,m=20function<void(int, int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);fc(10, 20); auto fn_half = std::bind (my_divide,std::placeholders::_1,2); cout<<fn_half(4)<<endl; auto fn_half1 = std::bind (&my_divide,std::placeholders::_1,2); cout<<fn_half1(4)<<endl; return 0;
}
对于&a和a都是可以正确编译执行,结果正确,往常我总是用&a的写法,传的是指针进去,所以见到绑定成员函数,第二个参数传的是a,本身对象而感到困惑
查阅资料得出:
这个是bind关于绑定成员函数的源码
template<typename _Result, typename _Func, typename... _BoundArgs>
struct _Bindres_helper: _Bind_check_arity<typename decay<_Func>::type, _BoundArgs...>
{typedef typename decay<_Func>::type __functor_type;typedef _Bind_result<_Result,__functor_type(typename decay<_BoundArgs>::type...)>type;
};template<typename _Result, typename _Func, typename... _BoundArgs>
inline
typename _Bindres_helper<_Result, _Func, _BoundArgs...>::type
bind(_Func&& __f, _BoundArgs&&... __args)
{typedef _Bindres_helper<_Result, _Func, _BoundArgs...> __helper_type;return typename __helper_type::type(std::forward<_Func>(__f),std::forward<_BoundArgs>(__args)...);
}
在这个模板函数中,_Bindres_helper 类模板用于根据 _Func 和 _BoundArgs 推导绑定结果的类型。它还使用 typename decay<_Func>::type 去除 _Func 的引用和修饰符,以便获得准确的函数类型。然后它返回创建的绑定对象的实例。
综上所述,当调用 std::bind 绑定成员函数时,需要传入成员函数的指针(或可调用对象)、成员函数所属的对象(或指向该对象的指针)、以及任何要为成员函数提供的参数。通过源码中的模板函数重载选择正确的实现,并返回创建的绑定对象。
bind(_Func&& __f, _BoundArgs&&... __args)
怎么做到绑定成员函数指针,和对象或对象指针的,,还有临时变量,右值等等
是因为用到了&&右值引用和引用折叠
- 对于
_Func&& __f这个参数,如果传递给std::bind的是一个右值,那么_Func将会被推导为该右值的类型,同时右值引用保持原样,因此可以接收对象的右值引用。 - 如果传递给
std::bind的是一个左值,根据引用折叠规则,_Func将被推导为左值的引用类型,即_Func&。这意味着它可以接收对象的左值引用。
同样地,在 _BoundArgs&&... __args 这个参数中,根据引用折叠规则,如果传递给 std::bind 的是右值,那么 _BoundArgs 和 __args 将会被推导为右值引用类型,并保持原样。这意味着它们可以接收对象的右值引用列表。如果传递给 std::bind 的是左值,那么它们将被推导为左值引用类型,即 _BoundArgs& 和 __args&。这意味着它们可以接收对象的左值引用列表。
综上所述,通过引用折叠规则和模板推导,std::bind 函数的参数可以接收对象和对象的指针(作为左值引用)
但是但是!虽然
auto boundFunc = std::bind(&MyClass::printMessage, obj, "Hello, world!");,虽然参数传递了一个对象 obj(而不是对象的指针或引用),但这个代码仍然能够编译和运行,并且没有出现错误。这是因为 std::bind 函数可以自动推断对象 obj 的正确类型。std::bind 函数使用了完美转发(perfect forwarding)来处理参数,因此它能够处理对象按值传递的情况。
std::bind 函数在内部会对对象 obj 进行一次拷贝,以便将其绑定到成员函数 MyClass::printMessage。但是,请注意,如果对象很大或者具有复杂的拷贝语义,就需要谨慎使用这种方式。
为了确保代码的健壮性和可维护性,建议在使用 std::bind 时,仍然传递指向对象的指针或引用作为 std::bind 的参数。这样可以更清晰地表达意图,并避免潜在的拷贝开销或对象生命周期问题。所以,下面的代码更推荐:
传指针的方式:
MyClass obj;
auto boundFunc = std::bind(&MyClass::printMessage, &obj, "Hello, world!");
传引用的方式:
MyClass obj;
auto boundFunc = std::bind(&MyClass::printMessage, std::ref(obj), "Hello, world!");
建议阅读:
【C++】C++11的std::function和std::bind用法详解_Yngz_Miao的博客-CSDN博客
C++ STL初识:bind封装函数讲解 - hugeYlh - 博客园 (cnblogs.com)
C++11新特性——std::bind参数绑定_山河君的博客-CSDN博客