How it works...

In the example from the previous section, we have defined a named lambda expression, that is, a lambda expression that has its closure assigned to a variable. This variable is then passed as an argument to the std::accumulate() function. This general algorithm takes the begin and the end iterators that define a range, an initial value to accumulate over, and a function that is supposed to accumulate each value in the range to the total. This function takes a first parameter representing the currently accumulated value and a second parameter representing the current value to accumulate to the total, and it returns the new accumulated value.

Note that I did not use the term add because this can be used for other things than just adding. It can also be used for calculating a product, concatenating, or other operations that aggregate values together.

The two calls to std::accumulate() in this example are almost the same, only the types of the arguments are different:

  • In the first call, we pass iterators to a range of integers (from a vector<int>), 0 for the initial sum and a lambda that adds two integers and returns their sum. This produces a sum of all integers in the range; for this example, it is 22.
  • In the second call, we pass iterators to a range of strings (from a vector<string>), an empty string for the initial value, and a lambda that concatenates two strings by adding them together and returning the result. This produces a string that contains all the strings in the range put together one after an other; for this example, the result is "hello world!".

Though generic lambdas can be defined anonymously in the place where they are called, it does not really make sense because the very purpose of a generic lambda (that is basically, as mentioned earlier, a lambda expression template) is to be reused, as shown in the example from the How to do it... section.

When defining this lambda expression used with multiple calls to std::accumulate(), instead of specifying concrete types for the lambda parameters (such as int or std::string) we used the auto specifier and let the compiler deduce the type. When encountering a lambda expression that has the auto specifier for a parameter type, the compiler generates an unnamed function object that has a call operator template. For the generic lambda expression in this example, the function object would look like this:

    struct __lambda_name__ 
{
template<typename T1, typename T2>
auto operator()(T1 const s, T2 const n) const { return s + n; }

__lambda_name__(const __lambda_name__&) = default;
__lambda_name__(__lambda_name__&&) = default;
__lambda_name__& operator=(const __lambda_name__&) = delete;
~__lambda_name__() = default;
};

The call operator is a template with a type parameter for each parameter in the lambda that was specified with auto. The return type of the call operator is also auto, which means the compiler will deduce it from the type of the returned value. This operator template will be instantiated with the actual types the compiler will identify in the context where the generic lambda is used.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset