考虑以下C++14的lambda使用,它返回其实参是否在最小值(lowVal
)和最大值(highVal
)之间的结果,其中lowVal
和highVal
是局部变量
auto betweenL =
[lowVal, highVal](const auto& val) //C++14
{ return lowVal <= val && val <= highVal; };
使用std::bind
可以表达相同的内容,但是该构造是一个通过晦涩难懂的代码来保证工作安全性的示例:
using namespace std::placeholders; //同上
auto betweenB =
std::bind(std::logical_and<>(), //C++14
std::bind(std::less_equal<>(), lowVal, _1),
std::bind(std::less_equal<>(), _1, highVal));
在C++11中,我们必须指定要比较的类型,然后std::bind
调用将如下所示:
auto betweenB =
std::bind(std::logical_and<bool>(), //C++11版本
std::bind(std::less_equal<int>(), lowVal, _1),
std::bind(std::less_equal<int>(), _1, highVal));
当然,在C++11中,lambda也不能采用auto
形参,因此它也必须指定一个类型:
auto betweenL = //C++11版本
[lowVal, highVal] (int val)
{ return lowVal <= val && val <= highVal; };
lambda版本不仅更短,而且更易于理解和维护
假设我们有一个函数可以创建Widget
的压缩副本
enum class CompLevel { Low, Normal, High }; //压缩等级
Widget compress(const Widget& w, //制作w的压缩副本
CompLevel lev);
并且我们想创建一个函数对象,该函数对象允许我们指定Widget w
的压缩级别。这种使用std::bind
的话将创建一个这样的对象:
Widget w;
auto compressRateB = std::bind(compress, w, _1);
std::bind
总是拷贝它的实参,但是调用者可以使用引用来存储实参,这要通过应用std::ref
到实参上实现
auto compressRateB = std::bind(compress, std::ref(w), _1);
结果就是compressRateB
行为像是持有w
的引用而非副本
然而在lambda方法中,其中w
是通过值还是通过引用捕获是显式的:
auto compressRateL = //w是按值捕获,lev是按值传递
[w](CompLevel lev)
{ return compress(w, lev); };
同样明确的是形参是如何传递给lambda的。在这里,很明显形参lev
是通过值传递的
compressRateL(CompLevel::High); //实参按值传递
但是在对由std::bind
生成的对象调用中
compressRateB(CompLevel::High); //实参如何传递?
答案是传递给bind
对象的所有实参都是通过引用传递的,因为此类对象的函数调用运算符使用完美转发
在C++11中,可以在两个受约束的情况下证明使用std::bind
是合理的
- 移动捕获。C++11的lambda不提供移动捕获,但是可以通过结合lambda和
std::bind
来模拟 - 多态函数对象。因为bind对象上的函数调用运算符使用完美转发,所以它可以接受任何类型的实参,当你要绑定带有模板化函数调用运算符的对象时,此功能很有用。
例如
class PolyWidget {
public:
template<typename T>
void operator()(const T& param);
};
std::bind
可以如下绑定一个PolyWidget
对象
PolyWidget pw;
auto boundPW = std::bind(pw, _1);
boundPW
可以接受任意类型的对象了
boundPW(1930); //传int给PolyWidget::operator()
boundPW(nullptr); //传nullptr给PolyWidget::operator()
boundPW("Rosebud"); //传字面值给PolyWidget::operator()
这一点无法使用C++11的lambda做到。但是,在C++14中,可以通过带有auto
形参的lambda轻松实现
auto boundPW = [pw](const auto& param) //C++14
{ pw(param); };
请记住:
- 与使用
std::bind
相比,lambda更易读,更具表达力并且可能更高效 - 只有在C++11中,
std::bind
可能对实现移动捕获或绑定带有模板化函数调用运算符的对象时会很有用