为什么在C++14中对lambdas使用std::bind
Why use std::bind over lambdas in 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"
与之相当的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中是多余的?
Scott Meyers对此进行了演讲。这就是我所记得的:
在C++14中,没有任何有用的绑定可以做的事情不能用lambda来做。
然而,在C++11中,有一些事情是lambdas无法完成的:
-
创建lambda时,在捕获时不能移动变量。变量总是被捕获为lvalue。对于绑定,您可以写:
auto f1 = std::bind(f, 42, _1, std::move(v));
-
无法捕获表达式,只能捕获标识符。对于绑定,您可以写:
auto f1 = std::bind(f, 42, _1, a + b);
-
重载函数对象的参数。问题中已经提到了这一点。
- 无法完善论点
在C++中,所有这些都是可能的。
-
移动示例:
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)); };
绑定的一些缺点:
-
绑定按名称绑定,因此,如果您有多个同名函数(重载函数),则绑定不知道该使用哪一个。下面的例子不会编译,而lambdas不会有问题:
void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
- 使用绑定函数时,内联的可能性较小
另一方面,理论上lambdas可能会生成比绑定更多的模板代码。因为对于每个lambda,都会得到一个唯一的类型。对于bind,只有当您有不同的参数类型和不同的函数时(我想在实践中,使用相同的参数和函数多次绑定的情况并不常见)。
乔纳森·韦克利在回答中提到的实际上是不使用绑定的又一个原因。我不明白你为什么要默默地忽略争论。
有时它只是更少的代码。考虑一下:
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
对我来说,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);
另一个区别是必须复制或移动要绑定的参数,而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&)
签名的情况下解决使用上述绑定的问题。
只是将@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');
}
}
在编译器资源管理器上测试
- 使用 std::应用于 std::bind
- PCL:当我在setConditionFunction中使用std::bind 时,没有合适的转换函数
- std::bind on statd::array 的运算符 []
- std::bind c++ in if statement
- std::bind 是否实现了 std::ref 和 std::cref 来消除函数调用的歧义?
- C++ 事件管理器的回调,使用 std::function 和 std:bind 以及派生类作为参数
- 试图克服 std::bind 编译错误
- c++ std::bind within function
- std::bind to void* to std::function
- std::bind 和 std::函数术语不值为接受 0 个参数?
- 在调用 std::bind 的产品后意外调用析构函数
- 使用 object 中的方法调用带有 std::bind 和 std::function.target 的 C 样式函数
- 我们应该在使用 std::bind 应用之前检查一个不为空的函数吗?
- 为什么 std::bind 静态类型检查传递给函数的参数?
- 对函数库中的语法感到困惑 std::bind
- 在模板类成员上使用 std::bind
- 如何将参数从函数传递给 std::bind
- 如何在C++中使用 std::bind 函数作为信号处理程序?
- std::bind() 参数列表中函子的执行顺序(可能与函数参数的求值顺序无关)
- 在模板函数参数中使用 std::bind