参考:bind-vs-lambda
参考:why-use-stdbind-over-lambdas-in-c14
参考:Lambdas vs. std::bind in C++11 and C++14
In C++98, such binding was accomplished via std::bind1st and std::bind2nd. TR1 added std::tr1::bind, which was promoted to std::bind in C++11. But C++11 also introduced lambda expressions, and they’re slated to become even more powerful in C++14. That means that there are now two mechanisms in C++ for binding functions to arguments for later calls: std::bind and lambda expressions
在C++11之前,boost里就已经提供了boost::bind
和boost::lambda
两个函数,在C++11到来之后,boost的bind部分代码合到了std::bind
里,lambda部分则合到了C++的lambda代码里,从此使用lambda更加方便了。自从C++11,用到bind的代码就很少了,大多数都可以用lambda表达式来替代了。
参考:Lambda expressions in C++
lambda表达式、简称为lambda,其实就是匿名函数,lambda表达式的写法最多可以有六个部分:
[]
()
对应的参数列表mutable
关键字throw()
对应的抛出异常{...}
其中,2、3、4、5都是可以省略不写的,比如一个完整的lambda函数写法:
// [=]号代表里面需要捕获的参数都是值传递进来的
[=] () mutable throw() ->int
{
int n = x + y;
x = y;
y = n;
return n;
}
关于Capture Clause,[]
代表函数不需要任何额外捕获的变量,函数需要的变量都通过()
参数列表传入,[=]
代表用到的非参数列表里的其他变量,都是值传递的,[&]
则全是引用传递的,还可以有特殊情况的写法:
// total变量是引用捕获, factor是值捕获
[&total, factor]
// 与上相反
[factor, &total]
// 除了factor是值捕获, 其他都是引用捕获
[&, factor]
// 除了total是引用捕获, 其他都是值捕获
[=, &total]
举一些例子:
// 例一: 按照绝对值大小排序
std::sort(x, x + n, [](float a, float b) { return (std::abs(a) < std::abs(b)); });
参考:How does generic lambda work in C++14?
起因是在写游戏引擎的时候看到这么一行代码:
m_Window->SetEventCallback([this](auto&&... args) -> decltype(auto)
{
return this->Application::OnEvent(std::forward<decltype(args)>(args)...);
});
这里用到的auto&&...
貌似涉及到了模板元编程,但我又根本没有看到模板的<>
,查了一下,这是Generic Lambda的使用范围,它是编译器为我们生成的模板。
先来看看什么是Generic Lambda:
当Lambda的参数列表里出现至少一个auto类型的参数,那么此Lambda就是一个Generic Lambda表达式,比如:
// 1
auto glambda = [] (auto a) { return a; };
// 2
auto f = [](auto a, auto b) { return a + b; };
std::cout << f(1, 4) << std::endl;// print 5
std::cout << f(string("ff"), string("aa")) << std::endl;// print ffaa
拿第一个例子来说,它会被编译器更改为:
class /* unnamed */
{
public:
template<typename T>
T operator () (T a) const { return a; }
};
generic lambdas are just syntactic sugar for unique, unnamed functors with a templated call operator
总的来说,泛型lambda就是一种syntactic sugar,是编译器创建的带模板operator的匿名functor。那么再回到原本的问题,下面这个lambda表达式为:
[this](auto&&... args) -> decltype(auto)
{
return this->Application::OnEvent(std::forward<decltype(args)>(args)...);
}
可以翻译为:
class /* unnamed */
{
public:
template<typename ...args>
T operator () (T&&... args) const { this->Application::OnEvent(std::forward<decltype(args)>(args)...); }
};
这里直接给两个结论:
std::bind
,不可以使用lambda表达式std::bind
都可以用lambda表达式来代替,因为泛型Lambda的出现让Lambda开始支持polymorphic关于第一点,有这么一些区别:
std::bind
可以使用右值,比如auto f1 = std::bind(f, 42, _1, std::move(v));
Expressions can't be captured, only identifiers can
,而std::bind可以写:auto f1 = std::bind(f, 42, _1, a + b);
std::bind
支持Overloading arguments for function objects
最后贴一些不太重要的东西
下面是一个C++11使用bind的例子:
struct foo
{
// 定义一个functor
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++0x lambdas are monomorphic, while bind can be polymorphic.
在C++11(或者C++0x)里,这样的代码是编译错误的,因为此时的lambda表达式是单态的
auto f = [](auto a, auto b) { cout << a << ' ' << b; }
f("test", 1.2f);
本来我打算用VS2017编译这段代码的,发现能够正常运行,查了下,应该VS2017最低也是用的C++14,所以我找了个在线C++11网站执行这段代码,编译报错,信息如下:
$g++ -std=c++11 -o main *.cpp
main.cpp: In function 'int main()':
main.cpp:6:14: error: use of 'auto' in lambda parameter declaration only available with -std=c++14 or -std=gnu++14
auto f = [](auto a, auto b) { cout << a << ' ' << b; };
^~~~
main.cpp:6:22: error: use of 'auto' in lambda parameter declaration only available with -std=c++14 or -std=gnu++14
auto f = [](auto a, auto b) { cout << a << ' ' << b; };
^~~~
main.cpp:7:16: error: no match for call to '(main()::) (const char [5], float)'
f("test", 1.2f);
^
main.cpp:7:16: note: candidate: 'void (*)(int, int)'
main.cpp:7:16: note: conversion of argument 2 would be ill-formed:
main.cpp:7:4: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
f("test", 1.2f);
^~~~~~
main.cpp:6:28: note: candidate: 'main()::'
auto f = [](auto a, auto b) { cout << a << ' ' << b; };
^
main.cpp:6:28: note: conversion of argument 1 would be ill-formed:
main.cpp:7:4: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
f("test", 1.2f);
^~~~~~
报错信息,显示,C++11的lambda函数的参数列表里,参数类型不可以为auto