为什么在C ++ 14中使用std :: bind over lambdas?

在C ++ 11之前,我使用了boost::bindboost::lambdabind部分成为标准库( std::bind ),另一部分成为核心语言(C ++ lambda)的一部分,并使lambda的使用更容易。 现在,我很less使用std::bind ,因为我几乎可以用C ++ lambda来做任何事情。 有一个有效的用于std::bind用例我可以想到:

 struct foo { typedef void result_type; template < typename A, typename B > void operator()(A a, B b) { cout << a << ' ' << b; } }; auto f = bind(foo(), _1, _2); f( "test", 1.2f ); // will print "test 1.2" 

这相当于C ++ 14

 auto f = []( auto a, auto b ){ cout << a << ' ' << b; } f( "test", 1.2f ); // will print "test 1.2" 

更短,更简洁。 (在C + + 11中,由于自动参数,这还没有工作。)有没有其他有效的用例为std::bind击败C ++的lambdas替代或是std::bind多余的C + + 14?

斯科特·迈耶斯(Scott Meyers)就此发表了谈话 这是我记得的:

在C ++ 14中没有什么有用的绑定可以做,也不能用lambdas做。

然而在C ++ 11中有一些不能用lambdas完成的事情:

  1. 在创buildlambdaexpression式时,您不能移动variables。 variables总是作为左值捕获。 对于绑定,你可以写:

     auto f1 = std::bind(f, 42, _1, std::move(v)); 
  2. expression式不能被捕获,只有标识符可以。 对于绑定,你可以写:

     auto f1 = std::bind(f, 42, _1, a + b); 
  3. 为函数对象重载参数。 这个问题已经提到过了。

  4. 不可能完善的论点

在C ++ 14中所有这些可能的。

  1. 移动示例:

     auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); }; 
  2. expression示例:

     auto f1 = [sum = a + b](auto arg) { f(42, arg, sum); }; 
  3. 看问题

  4. 完美的转发:你可以写

     auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); }; 

绑定的一些缺点:

  • 绑定绑定的名称和结果,如果你有多个具有相同名称的函数(重载函数)绑定不知道使用哪一个。 下面的例子不会编译,而lambdas不会有问题:

     void f(int); void f(char); auto f1 = std::bind(f, _1, 42); 
  • 使用绑定函数时,内联的可能性较小

另一方面,lambdas在理论上可能会产生比绑定更多的模板代码。 因为对于每个lambda你得到一个独特的types。 对于绑定,只有当你有不同的参数types和一个不同的函数(我猜在实践中,它不会经常发生,你几次绑定相同的参数和函数)。

Jonathan Wakely在答复中提到的其实是另一个不使用绑定的理由。 我不明白你为什么要默默地忽略理由。

std::bind仍然可以做一件事多态lambda不能:调用重载函数

 struct F { bool operator()(char, int); std::string operator()(char, char); }; auto f = std::bind(F(), 'a', std::placeholders::_1); bool b = f(1); std::string s = f('b'); 

由bindexpression式创build的调用包装函数根据所提供的参数调用不同的函数,C ++ 14多态lambdaexpression式的闭包可以采用不同types的参数,但不能采用不同数量的参数,并始终调用)closures时的相同function。 更正:请参阅下面的评论

std::bind返回的包装也可以调用太多的参数,它会忽略它们,而由lambda创build的闭包将诊断尝试传递太多的参数…但我不认为这是一个好处std::bind 🙂

对于我来说, std::bind的有效用法就是明确说明我使用了一个成员函数作为谓词。 也就是说,如果我只是调用一个成员函数,它就是绑定的。 如果我做了额外的东西(除了调用memeber函数),它是一个lambda:

 using namespace std; auto is_empty = bind(&string::empty, placeholders::_1); // bind = just map member vector<string> strings; auto first_empty = any_of(strings.begin(), strings.end(), is_empty); auto print_non_empty = [](const string& s) { // lambda = more than member if(s.empty()) // more than calling empty std::cout << "[EMPTY]"; // more than calling empty else // more than calling empty std::cout << s; // more than calling empty }; vector<string> strings; for_each(strings.begin(), strings.end(), print_non_empty); 

另一个区别是绑定参数必须被复制或移动,而lambda可以使用由引用捕获的variables。 看下面的例子:

 #include <iostream> #include <memory> void p(const int& i) { std::cout << i << '\n'; } int main() { std::unique_ptr<int> f = std::make_unique<int>(3); // Direct p(*f); // Lambda ( ownership of f can stay in main ) auto lp = [&f](){p(*f);}; lp(); // Bind ( does not compile - the arguments to bind are copied or moved) auto bp = std::bind(p, *f, std::placeholders::_1); bp(); } 

不知道是否有可能解决这个问题,使用bind而不改变void p(const int&)的签名。