Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A generic lambda is a lambda expression whose function call operator is a member template. This occurs when the lambda uses auto type specifiers in its parameter list (introduced in C++14) or defines an explicit template parameter list (introduced in C++20). It instructs the compiler to synthesize a closure type containing a function call operator member template (operator()).

Syntax Visualization

C++14: Implicit Template Parameters Using auto in the parameter list implicitly makes the lambda generic. Each auto parameter represents a distinct, unnamed template type parameter.
auto generic_lambda = [](auto x, auto y) {
    return x + y;
};
C++20: Explicit Template Parameter Lists C++20 introduced the ability to explicitly declare template parameters within angle brackets <> immediately following the lambda introducer [].
auto explicit_generic_lambda = []<typename T, typename U>(T x, U y) {
    return x + y;
};

Compiler Translation (Under the Hood)

When the compiler encounters a generic lambda, it synthesizes an unnamed closure class. The generic parameters are translated into a templated operator(). The C++14 generic lambda [](auto x, auto y) { return x + y; } is structurally equivalent to the following compiler-generated class:
class __CompilerGeneratedClosure {
public:
    // The call operator is a function call operator member template
    template <typename T1, typename T2>
    auto operator()(T1 x, T2 y) const {
        return x + y;
    }
    
    // State (captures) would be stored here as member variables
};

Type Deduction and Qualifiers

The auto keyword in a generic lambda parameter list strictly follows template argument deduction rules. By default, auto deduces the base type by value, stripping top-level const, volatile, and reference qualifiers. To modify the parameter type, standard reference and cv-qualifiers must be explicitly applied to the auto specifier. The auto placeholder deduces the base type T, while the explicit qualifiers determine the final parameter type:
auto by_value     = [](auto x) {};        // 'auto' deduces T, parameter type is T
auto by_reference = [](auto& x) {};       // 'auto' deduces T, parameter type is T&
auto by_const_ref = [](const auto& x) {}; // 'auto' deduces T, parameter type is const T&
auto perfect_fwd  = [](auto&& x) {};      // 'auto' deduces T (or ref for lvalues), parameter type is T&&

Variadic Generic Lambdas

Generic lambdas fully support parameter packs, enabling the creation of variadic closures. This is achieved by combining auto with the ellipsis ... operator.
auto variadic_lambda = [](auto&&... args) {
    // 'args' is a function parameter pack of forwarding references
};
This translates to a closure class with a variadic member function template:
class __VariadicClosure {
public:
    template <typename... Ts>
    auto operator()(Ts&&... args) const {
        // ...
    }
};

C++20 Explicit Template Parameter Lists

The C++20 explicit template syntax resolves specific structural limitations of the C++14 auto syntax, primarily regarding type enforcement and type extraction. 1. Enforcing Identical Types In C++14, [](auto a, auto b) deduces two independent types. Enforcing that both arguments are of the exact same type required static_assert and std::is_same_v. C++20 allows direct enforcement:
auto strict_lambda = []<typename T>(T a, T b) {
    // Both 'a' and 'b' are guaranteed to be of the exact same type 'T'
};
2. Accessing the Underlying Type In C++14, extracting the type of an auto parameter for internal declarations required decltype. If the parameter was passed by reference, decltype yielded a reference type, which lacks nested types (like ::value_type), necessitating type traits like std::decay_t or std::remove_reference_t. C++20 provides direct access to the type identifier.
// C++14 approach
auto cpp14_lambda = [](auto& container) {
    // decltype(container) is a reference type. 
    // std::decay_t is required to access the nested ::value_type.
    using ValueType = typename std::decay_t<decltype(container)>::value_type;
};

// C++20 approach
auto cpp20_lambda = []<typename T>(std::vector<T>& container) {
    T new_element; // Direct access to the underlying type T
};

C++20 Constrained Generic Lambdas (Concepts)

C++20 integrates Concepts with generic lambdas, allowing developers to constrain the types that the lambda can accept. This can be applied using either the constrained auto syntax or explicit template parameter lists. Constrained auto Syntax:
#include <concepts>

auto constrained_auto_lambda = [](std::integral auto x, std::floating_point auto y) {
    // 'x' must be an integral type, 'y' must be a floating-point type
};
Constrained Explicit Template Syntax:
#include <concepts>

auto constrained_explicit_lambda = []<std::integral T>(T x, T y) {
    // Both 'x' and 'y' must be of the exact same integral type 'T'
};
If a constrained generic lambda is invoked with arguments that do not satisfy the specified concepts, the compiler rejects the invocation during overload resolution.
Master C++ with Deep Grasping Methodology!Learn More