1.很重要的原因是lambda表达式更加简洁易读。
实现一个函数,它返回其实参是否在最小值(low
)和最大值(high
)之间的结果,其中low
和high
是局部变量。
int low=10;
int high=345;
//C++14 使用lamdba
auto betweenL =[low, high](const auto& val){ return low <= val && val <= high;};
//使用std::bind实现相同效果
using namespace std::placeholders;
//c++14
auto betweenB =
std::bind(std::logical_and<>(),
std::bind(std::less_equal<>(), low, _1),
std::bind(std::less_equal<>(), _1, high));
//c++11
auto betweenB =
std::bind(std::logical_and<bool>(),
std::bind(std::less_equal<int>(), low, _1),
std::bind(std::less_equal<int>(), _1, high));
可以看到,lambda版本不仅更短,而且更易于理解和维护。
2.lambda 可以指定值捕获和引用捕获,而std::bind总会按值拷贝实参,要按引用传递则需使用std::ref;std::bind
生成的对象调用中,实参如何传递也是疑惑,难以从代码中看出来。
void f(const A&,int& val);
using namespace std::placeholders;
A a;
//lamdba
auto l=[a](int v){f(a,v);}; //a是按值捕获,v是按值传参
//调用ladmba
int c=23;
l(c); //实参c是按值传参
//std::bind
auto b = std::bind(f, a, _1);
auto b2=std::bind(f,std::ref(a),_1);
//调用std::bind
b(c); 实参c是如何传递??
这里要解决两个疑惑:
1.std::bind中的a是按值捕获的还是按引用捕获的。
2.std::bind中的_1占位符是按值传参还是按引用传参的。
问题1,lamdba可以很容易地看出来是按值捕获的。而使用std::bind不好看出来a是按值捕获还是按引用捕获的。
答案是std::bind中a是按值捕获的。std::bind
总是拷贝它的实参。
但是调用者也可以使用引用来存储实参,这要通过应用std::ref
到实参上实现,b2就是按引用捕获的。
所以这样一对比,还是使用lamdba方便易懂。
问题2,lamdba中很容易看到实参c是按值传参的。因为lamdba表达式中小括号内是(int val),这是按值传参。
而std::bind中,占位符_1对std::bind
的调用中没有任何迹象表明是按值传参还是按引用传参的。
唯一的方法是记住std::bind
的工作方式。答案是传递给bind对象的所有实参都是通过引用传递的,因为此类对象的函数调用运算符使用完美转发。
又一对比,使用lamdba真是方便易懂呀。
3.lambda 可以正常使用重载函数,而 std::bind 无法区分重载版本,为此必须指定对应的函数指针类型。lambda 闭包类的 operator() 采用的是能被编译器内联的常规的函数调用,而 std::bind 采用的是一般不会被内联的函数指针调用,这意味着 lambda 可能比 std::bind 运行得更快。
void f(int){}
void f(double){}
auto b1=[](){f(1);};
auto b2=[](){f(2.3);};
auto g = std::bind(f, 1); // error
auto g = std::bind(static_cast<void (*)(int)>(f), 1); // OK
4.实参绑定的是 std::bind 返回的对象,而非内部的函数
void f(std::chrono::steady_clock::time_point t, int i) {
std::this_thread::sleep_until(t);
std::cout << i<<std::endl;
}
int main() {
using namespace std::chrono_literals;
using namespace std::placeholders;
auto l = [](int i) { f(std::chrono::steady_clock::now() + 8s, i); };
l(2); // 8 秒后打印 2
auto b = std::bind(f, std::chrono::steady_clock::now() + 8s, _1);
b(2); // 调用 std::bind 后的 8 秒打印 2,而非调用 f 后的 8 秒
}
在lambda中,表达式steady_clock::now() + 8s
显然是函数f的实参。调用函数f时将对其进行计算。可以理解:我们希望在调用函数f后8s响铃。
这个情况为什么使用 std::bind不能达到我们的要求的?
在std::bind
调用中,将steady_clock::now() + 8s
作为实参传递给了std::bind
,而不是函数f。这意味着将在调用std::bind
时对表达式进行求值,并且该表达式产生的时间将存储在产生的bind对象中。结果,警报器将被设置为在调用std::bind
后8s发出声音,而不是在调用f
后8s
发出。
解决办法是延迟到调用绑定的函数时再计算表达式值,这可以通过在内部再嵌套一个 std::bind 来实现。复杂繁琐。
auto b = std::bind(f,
std::bind(std::plus<>{}, std::chrono::steady_clock::now(),8),
_1);
5.在C++14中,没有std::bind
的合理用例。
但是,在C++11中,可以在两个受约束的情况下证明使用std::bind
是合理的:
- 移动捕获。C++11的lambda不提供移动捕获,但是可以通过结合lambda和
std::bind
来模拟。 - 多态函数对象。因为bind对象上的函数调用运算符使用完美转发,所以它可以接受任何类型的实参。当你要绑定带有模板化函数调用运算符的对象时,此功能很有用。
最终结论:
综上所述,可以使用lamdba代替std::bind。