73

在 C++11 之前我用过boost::bind很多boost::lambdabind一部分进入了标准std::bind库(现在,我几乎不使用std::bind,因为我几乎可以用 C++ lambda 做任何事情。我能想到一个有效的用例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 中,由于 auto 参数,这还不起作用。)是否有任何其他有效std::bind的用例可以击败 C++ lambdas 替代方案,或者std::bind对于 C++14 来说是多余的?

4

5 回答 5

88

Scott Meyers 就此事发表了演讲。这是我记得的:

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

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

  1. 在创建 lambda 时,您不能在捕获时移动变量。变量总是被捕获为左值。对于绑定,您可以编写:

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

    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. 表达式示例:

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

  4. 完美转发:可以写

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

绑定的一些缺点:

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

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

另一方面,理论上 lambda 可能会生成比绑定更多的模板代码。因为对于每个 lambda,你都会得到一个唯一的类型。对于绑定,只有当您具有不同的参数类型和不同的函数时(我猜在实践中,您使用相同的参数和函数绑定多次并不会经常发生)。

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

于 2013-07-09T09:49:38.813 回答
7

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

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

然后

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

与拉姆达

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

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

于 2018-02-15T11:46:23.387 回答
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);
于 2013-06-28T12:48:43.753 回答
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&).

于 2017-01-30T14:06:03.823 回答
0

只是将@BertR 对这个答案的评论扩展为可测试的东西,尽管我承认我无法使用 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');
    }
}

编译器资源管理器中测试

于 2018-08-10T16:00:55.997 回答