Standard C++14 makes C++11 more complete as C++11 has a lot of imperfections. C++14 not only amends C++11 but also proposes many new features, among which the most eye-catching one is the supplements to lambda-expressions.
Lambda-expression is the most interesting feature in C++11 that challenges the long-used way of defining functions. C++14 proposes two major supplements to this famous feature.
The first supplement is called “generalized lambda-capture” that comes from proposals N3610 and N3648. This supplement is initially proposed for capture-by-move. Besides this new functionality, the supplement also makes capture lists more flexible. This supplement indicates that expressions are now allowed to appear in the capture lists of lambda-expressions. See the following example:
int x = 4;
auto y = [&r = x, x = x+1]()->int {
r += 2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.
In this example, you can find two variables r and x in the capture list. Variable r is captured by reference and initialized with the value of x. However, r is not declared in the parent scope of the lambda-expression, which is not permitted in C++11. In C++14, its type is deduced by its initialization expression. Here, r is an lvalue reference of x. The other variable x is declared in the parent scope of the lambda-expression, and it is captured by value and initialized with the value of x+1, so it is not the same x in the parent scope. The initial value of x in the lambda-expression is 5. After calling the above lambda-expression, we can get the final results of x and y that are 6 and 7 respectively.
The compiler achieves lambda-expressions by converting them to function objects. To help you better understand this new feature of lambda-expressions, you can try to convert the above lambda-expression to a function object.
The other supplement to lambda is called “generic lambda” that comes from N3559 and N3649. In C++14, the auto type specifier can be the specifier of lambda parameters to identify a generic lambda-expression. See the following example:
auto Identity = [](auto a) { return a; };
int three = Identity(3);
char const* hello = Identity("hello");
In this example, we define a generic lambda-expression and initialize Identity with the closure object. In the implementation, the compiler converts this generic lambda-expression to the following function object:
template<typename T>
class lambda {
public:
T operator(T t) { return t; }
};
When function Identity(3) is called, the generic lambda-expression is initialized to a lambda-expression whose parameter type is int. When Identity (“hello”) is called, the generic lambda-expression is initialized as its parameter type is const char*. Generic lambda expressions are acting as function templates.
A generic lambda-expression without a capture list can be converted to a pointer-to-function. When the pointer-to-function is invoked, it has the same effect as invoking the generic lambda-expression. See the following example:
auto Identity = [](auto a) { return a; };
int (*fpi)(int) = Identity;
In both C++11 and C++14, if a lambda-expression has a capture list, it cannot be converted to a pointer-to-function. This is a big difference between a lambda-expression and a traditional function: the former can have initial status while the latter cannot.