为什么在 C++14 中使用 std::bind 而不是 lambdas?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17363003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:07:50  来源:igfitidea点击:

Why use std::bind over lambdas in C++14?

c++lambdabindc++14

提问by Ralph Tandetzky

Before C++11 I used boost::bindor boost::lambdaa lot. The bindpart made it into the standard library (std::bind) the other part became part of the core language (C++ lambdas) and made the use of lambdas a lot easier. Nowadays, I hardly use std::bind, since I can do almost anything with C++ lambdas. There's one valid use-case for std::bindthat I can think of:

在 C++11 之前,我使用过boost::bindboost::lambda很多。该bind部分把它做成标准库(std::bind),另一部分变成了核心语言的一部分(C ++ lambda表达式),并提出了使用lambda表达式的轻松了不少。现在,我几乎不使用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"

The C++14 equivalent for that would be

对应的 C++14 将是

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

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::bindbeating the C++ lambdas alternative or is std::bindsuperfluous with C++14?

更短更简洁。(在 C++11 中,由于自动参数,这还不起作用。)是否有任何其他有效用例可以std::bind击败 C++ lambdas 替代方案,或者std::bind对 C++14 来说是多余的?

回答by BertR

Scott Meyers gave a talkabout this. This is what I remember:

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

In C++14 there is nothing useful bind can do that can't also be done with lambdas.

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

In C++11however there are some things that can't be done with lambdas:

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

  1. You can't move the variables while capturing when creating the lambdas. Variables are always captured as lvalues. For bind you can write:

    auto f1 = std::bind(f, 42, _1, std::move(v));
    
  2. Expressions can't be captured, only identifiers can. For bind you can write:

    auto f1 = std::bind(f, 42, _1, a + b);
    
  3. Overloading arguments for function objects. This was already mentioned in the question.

  4. Impossible to perfect-forward arguments
  1. 创建 lambda 表达式时,您不能在捕获时移动变量。变量总是被捕获为左值。对于绑定,您可以编写:

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

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

  4. 不可能完美转发论点

In C++14all of these possible.

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

  1. Move example:

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

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

  4. Perfect forwarding: You can write

    auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); };
    
  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)); };
    

Some disadvantages of bind:

bind 的一些缺点:

  • Bind binds by name and as a result if you have multiple functions with the same name (overloaded functions) bind doesn't know which one to use. The following example won't compile, while lambdas wouldn't have a problem with it:

    void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
    
  • When using bind functions are less likely to be inlined
  • 绑定按名称绑定,因此如果您有多个具有相同名称的函数(重载函数),则绑定不知道要使用哪个。下面的例子不会编译,而 lambdas 不会有问题:

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

On the other hand lambdas might theoretically generate more template code than bind. Since for each lambda you get a unique type. For bind it is only when you have different argument types and a different function (I guess that in practice however it doesn't happen very often that you bind several time with the same arguments and function).

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

What Jonathan Wakely mentioned in his answer is actually one more reason not to use bind. I can't see why you would want to silently ignore arguments.

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

回答by Jonathan Wakely

std::bindcan still do one thing polymorphic lambdas can't: invoke overloaded functions

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

The call wrapper created by the bind expression calls different functions depending on the arguments you give it, the closure from a C++14 polymorphic lambda can take different typesof arguments but can't take a different numberof arguments, and always invokes (specializations of) the same function on the closure. Correction: see the comments below

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

The wrapper returned by std::bindcan also be called with too manyarguments and it will ignore them, whereas a closure created by a lambda will diagnose attempts to pass too many arguments ... but I don't consider that a benefit of std::bind:)

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

回答by AlexTheo

Sometimes it is just less code. Consider this:

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

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

Then

然后

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

vs lambda

VS 拉姆达

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

I think that bind is easier to read here compared to the lambda which looks like a https://en.wikipedia.org/wiki/Brainfwor

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

回答by utnapistim

For me, a valid use for std::bindis to make it clear that I'm using a member function as a predicate. That is, if all I do is call a member function, it's bind. If I do extra stuff with the argument (besides calling a memeber function), it's a lambda:

对我来说,一个有效的用途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);

回答by Zitrax

Another difference is that arguments to bind must be copied or moved, while a lambda can use variables captured by reference. See example below:

另一个区别是绑定的参数必须被复制或移动,而 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();
}

Not sure if it's possible to workaround the issue to use bind above without changing the signature of void p(const int&).

不确定是否可以在不更改void p(const int&).

回答by Orwellophile

Just expanding @BertR's comment to this answerto something testable, though I confess I couldn't quite get a solution using std::forward<> to work.

只是将@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');
    }
}

Test at Compiler Explorer

编译器资源管理器中测试