在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"
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?
答案 0 :(得分:59)
在C ++ 14中,没有什么有用的绑定可以做到,也不能用lambdas完成。
在C ++中 11 但是有些事情不能用lambdas完成:
创建lambda时捕获时无法移动变量。变量总是被捕获为左值。对于bind,你可以写:
auto f1 = std::bind(f, 42, _1, std::move(v));
无法捕获表达式,只能识别标识符。对于bind,你可以写:
auto f1 = std::bind(f, 42, _1, a + b);
重载函数对象的参数。问题中已经提到过这一点。
在C ++中 14 所有这些都可能。
移动示例:
auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); };
表达示例:
auto f1 = [sum = a + b](auto arg) { f(42, arg, sum); };
见问题
完美转发:您可以写
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);
另一方面,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);});
的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');
}
}