C++11中的std::bind是对C++98中的std::bind1st 和std::bind2nd的改进和继承。而std::bind是在2005年的时候 作为标准库的一部分,起初是放在TR1规范中,拥有自己独立的命名空间std::tr1。因此很多C++程序员在很早就开始使用std::bind,所以如果要让这些C++程序员放弃对std::bind的使用,会多少有些不情愿。但是在C++11放弃对std::bind的使用是个不错的选择,因为在C++11中的lambda要比std::bind更好。而C++14对lambda进行了加强,加强后的lambda可以完全替换std::bind了。而lambda之所以要比std::bind要好主要有几个方面,第一就是可读性好,例如下面这个例子:
上面的lambda代码中,可以清晰的看到lambda函数setSoundL调用了setAlarm函数,并传入了两个参数。而到了C++14后借助于字面值类型可以使得上面的lambda表达式更加清晰。
通过字面值类型可以将hours(1)替换成1h,seconds(30)替换成30s,使得代码更加清晰。但是如果要使用std::bind的话代码会变成如下形式:
首先上面的代码不易读,看不出来调用关系和传参情况,还有 _1
这样的magic number存在。此外上面这个 std::bind
还存在另外一个问题就 是 steady_clock::now()+1h
的调用时机。理论上是在 setAlarm
被调用的时候才会执行 steady_clock::now()+1h
,但是如果使用 std::bind
会导致先计算 steady_clock::now()+1h
的值,然后保存在 std::bind
对象的内部,当 setAlarm
被调用的时候再把这个计算好的值传递过去,这就存在了时间差导致结果并不符合预期。修复这个问题的方法也很简单,只要让这个表达式延迟计算,在被调用的时候再执行即可,因此可以通过std::bind封装成一个std::function对象,然后在调用的时候执行这个function对象即可,代码如下:
上面的代码中 std::plus
是一个模版但是却省略了类型,这是C++14中引入的新特性,可以忽略类型部分,但是C++11中不支持这种形式,需要显示的给出要实例化的类型如下:
除了上文中提及到的不易读,容易造成错误等问题外,std::bind在遇到函数重载等时候也会出现问题。当上文中的setAlarm出现了重载函数,lambda版本的调用无需做任何修改就可以正常使用,但是std::bind却不行,因为std::bind并不能推断出调用的是哪个重载版本,因此必须显示的通过函数指针来指明,代码如下:
用lambda替换std::bind的第二个好处就是可以提升性能,lambda是直接调用目标函数,所以编译器可以根据目标函数的函数体大小进行有选择的内联,但是std::bind不可以,std::bind本质上是通过函数指针指向目标函数,然后在调用的函数通过函数指针来调用,所以无法被内联(因为内联是发生在编译期,但是指针的指向在运行期会被改变,因此函数指针指向的函数无法被内联)。
用lambda替换std::bind的第三个好处就是可以代码更容易被人理解,不会被误用,我们知道std::bind可以绑定参数,绑定的参数会被传递到std::bind对象的内部,以便在调用函数的时候可以传递过去,那么这些函数是如何传递到std::bind对象的内部呢?默认是通过传值的方式,但是从写法上来无法一眼看出,但是lambda不一样,它可以将引用的外部参数显示的指出来,表明是值传递还是引用传递,例如下面这个例子:
上面的代码中显示的列出了w,表明这是一个需要值传递到lambda内部的变量,但是如果使用std::bind的话是看不出那些变量会进行值传递,代码如下:
综上所述,std::bind相比于lambda来说,易读性差、不易于表达、容易错误、性能差。所以C++14中没有理由是用std::bind,建议使用lambda将其替换,但是在C++11中std::bind有两个用法是合理的,需要其存在。第一个就是使用std::bind模拟lambda的移动捕获,在C++11中只有值捕获和引用捕获,到了C++14才支持移动捕获。将参数移动到std::bind对象中,然后在lambda表达式中直接调用std::bind绑定的对象即可。第二个则是实现多态,C++11中的lambda的参数是不能使用auto的,也就是不支持通用lambda,只能接受特定类型,但是std::bind可以不受这个限制,例如下面这段代码:
带了 C++14
后lambda才开始支持auto参数,因此在C++11中在这种场景下,std::bind还是有用武之地的。
Tips
lambda
相比于std::bind
更易读、性能也更好。在
C++11
中std::bind
仅仅在模拟实现lambda init capture机制和绑定对象到一个模版函数中有其用武之地。