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

Ral*_*zky 56 c++ lambda bind c++14

在C++ 11之前我使用过boost::bind或者boost::lambda很多.该bind部分使其成为标准库(std::bind),另一部分成为核心语言(C++ lambdas)的一部分,并使lambdas的使用变得更加容易.如今,我几乎没用std::bind,因为我几乎可以用C++ lambdas做任何事情.std::bind我可以想到一个有效的用例:

struct foo
{
  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"
Run Code Online (Sandbox Code Playgroud)

相当于C++ 14的等价物

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

Much shorter and more concise. (In C++11 this does not work yet because of the auto parameters.) Is there any other valid use case for std::bind beating the C++ lambdas alternative or is std::bind superfluous with C++14?

Ber*_*rtR 65

斯科特迈耶斯谈到了这一点.这是我记得的:

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

但是在C++ 11中,有些事情不能用lambdas完成:

  1. 在创建lambdas时捕获时无法移动变量.变量总是被捕获为左值.对于绑定,您可以写:

    auto f1 = std::bind(f, 42, _1, std::move(v));
    
    Run Code Online (Sandbox Code Playgroud)
  2. 表达式无法捕获,只有标识符才能捕获.对于绑定,您可以写:

    auto f1 = std::bind(f, 42, _1, a + b);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 重载函数对象的参数.问题中已经提到过这一点.

  4. 不可能完美地转发争论

在C++ 14中所有这些都是可能的.

  1. 移动示例:

    auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 表达示例:

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

  4. 完美转发:你可以写

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

绑定的一些缺点:

  • 绑定按名称绑定,因此如果您有多个具有相同名称的函数(重载函数),bind不知道使用哪个函数.以下示例将不会编译,而lambdas不会有问题:

    void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用绑定函数时不太可能内联

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

Jonathan Wakely在他的回答中提到的实际上是不使用bind的另一个原因.我不明白你为什么要默默地忽略论点.


Jon*_*ely 29

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

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');
Run Code Online (Sandbox Code Playgroud)

由绑定表达式创建的调用包装器根据您提供的参数调用不同的函数,来自C++ 14多态lambda的闭包可以采用不同类型的参数但不能使用不同数量的参数,并且始终调用(闭合时功能相同的特殊化. 更正:见下面的评论

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

  • @Yakk:支持可变参数自动参数.所以它会变成类似:[](auto && ... args){F()(std :: forward <decltype(args)>(args)...);}; 请参阅http://isocpp.org/files/papers/N3649.html (13认同)
  • 你的`f`与`auto f = [](auto x)有什么不同{return F()('a',x);}`?或者是参数的唯一限制数量? (9认同)
  • 好的一点,那个lambda可以重现有用的部分,它只是无用的接受太多的args部分它不会那么做! (8认同)

Ale*_*heo 6

有时它只是更少的代码。考虑一下:

bool check(int arg1, int arg2, int arg3)
{
  return ....;
}
Run Code Online (Sandbox Code Playgroud)

然后

wait(std::bind(check,a,b,c));
Run Code Online (Sandbox Code Playgroud)

VS 拉姆达

wait([&](){return check(a,b,c);});
Run Code Online (Sandbox Code Playgroud)

我认为与看起来像https://en.wikipedia.org/wiki/Brainfuck的 lambda 相比,这里的 bind 更容易阅读

  • 请注意,您可以删除 lambda 定义中的空括号。 (3认同)
  • 它们并不等同。您的绑定通过值捕获,但您的 lambda 通过引用捕获。你最好不要陷入后一种情况! (2认同)