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

时间:2013-06-28 10:50:33

标签: c++ lambda bind c++14

在C ++ 11之前,我经常使用boost::bindboost::lambdabind部分使其成为标准库(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"

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?

6 个答案:

答案 0 :(得分:59)

斯科特迈耶斯对此提出了talk。这是我记得的:

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

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

  1. 创建lambda时捕获时无法移动变量。变量总是被捕获为左值。对于bind,你可以写:

    auto f1 = std::bind(f, 42, _1, std::move(v));
    
  2. 无法捕获表达式,只能识别标识符。对于bind,你可以写:

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

  4. 不可能完美地转发论点
  5. 在C ++中 14 所有这些都可能。

    1. 移动示例:

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

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

    4. 完美转发:您可以写

      auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); };
      
    5. bind的一些缺点:

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

        void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
        
      • 使用绑定功能时不太可能内联

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

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

答案 1 :(得分:29)

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');

绑定表达式创建的调用包装器根据您提供的参数调用不同的函数,来自C ++ 14多态lambda的闭包可以采用不同的类型参数但不能采用一个不同的 number 参数,并且总是在闭包上调用(特化)相同的函数。 更正:请参阅以下评论

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

答案 2 :(得分:2)

对我来说,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);

答案 3 :(得分:2)

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

bool check(int arg1, int arg2, int arg3)
{
  return ....;
}

然后

wait(std::bind(check,a,b,c));

vs lambda

wait([&](){return check(a,b,c);});

我觉得与看起来像https://en.wikipedia.org/wiki/Brainfuck

的lambda相比,bind更容易阅读

答案 4 :(得分:0)

另一个区别是必须复制或移动bind的参数,而lambda可以使用通过引用捕获的变量。见下面的例子:

#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();
}

不确定是否可以解决问题以使用上面的绑定而不更改void p(const int&)的签名。

答案 5 :(得分:0)

只是将@BertR对this answer的评论扩展到可以测试的地方,尽管我承认我无法完全使用std :: forward <>解决方案。

#include <string>
#include <functional>
using namespace std::string_literals;

struct F {
    bool        operator()(char c, int  i) { return c == i;  };
    std::string operator()(char c, char d) { return ""s + d; };
};

void test() {
    { // using std::bind
        auto f = std::bind(F(), 'a', std::placeholders::_1);
        auto b = f(1);
        auto s = f('b');
    }
    { // using lambda with parameter pack
        auto x = [](auto... args) { return F()('a', args...); };
        auto b = x(1);
        auto s = x('b');
    }
}

Compiler Explorer测试