C++14 Generic Lambda
Overview
C++11 introduced lambda expressions, but parameter types had to be explicitly specified. C++14 allows lambda parameters to use the auto keyword, making lambdas automatically generic — equivalent to a function object with template parameters. This eliminates the need to write multiple lambdas or explicit Functor classes for similar operations on different types.
Syntax
cpp
// C++11 lambda — parameter types must be explicit
auto add = [](int a, int b) { return a + b; };
// C++14 generic lambda — uses auto parameters
auto add = [](auto a, auto b) { return a + b; };
// Equivalent explicit template Functor
struct Add {
template <typename T, typename U>
auto operator()(T a, U b) const { return a + b; }
};Each auto parameter corresponds to a separate template parameter, and the compiler generates an operator() overload for each combination of argument types.
Code Examples
Basic Usage
cpp
#include <iostream>
#include <string>
int main() {
// Same lambda handles different types
auto print = [](auto const& val) {
std::cout << val << '\n';
};
print(42); // int
print(3.14); // double
print("hello"); // const char*
print(std::string("world")); // std::string
}With STL Algorithms
cpp
#include <algorithm>
#include <vector>
#include <string>
// Generic search: any container, any value type
auto contains = [](auto const& container, auto const& value) {
return std::find(container.begin(), container.end(), value)
!= container.end();
};
void demo() {
std::vector<int> vi = {1, 2, 3, 4, 5};
std::vector<std::string> vs = {"alpha", "beta", "gamma"};
contains(vi, 3); // true
contains(vs, std::string("beta")); // true
}Combining Generic Capture with Generic Parameters
cpp
#include <functional>
auto make_adder = [](auto x) {
// Returns a closure that captures the value of x
return [x](auto y) { return x + y; };
};
void demo() {
auto add5 = make_adder(5);
add5(3); // 8 — int
add5(2.5); // 7.5 — double
}Multi-parameter Generic Lambda with Perfect Forwarding
cpp
#include <utility>
#include <iostream>
auto perfect_call = [](auto&& func, auto&&... args) {
return std::forward<decltype(func)>(func)(
std::forward<decltype(args)>(args)...
);
};
void greet(const char* name, int times) {
for (int i = 0; i < times; ++i)
std::cout << "Hello, " << name << "!\n";
}
void demo() {
perfect_call(greet, "World", 3);
}How the Compiler Handles Generic Lambdas
The compiler transforms a generic lambda into a closure type where operator() is a member template:
cpp
// What you write:
auto lam = [](auto a, auto b) { return a + b; };
// What the compiler generates (simplified):
struct __closure_type {
template <typename T, typename U>
auto operator()(T a, U b) const { return a + b; }
};As a result, the same lambda instantiates different function bodies for different argument types.
Best Practices
- Prefer generic lambdas over redundant Functor classes: When behavior is simple and needs to be reused across types, generic lambdas are more concise than hand-written Functor classes.
- Understand the difference between
auto&&andauto: Pass-by-value copies; useauto const&orauto&&for pass-by-reference.auto const&orauto&&is recommended for generic code. - Avoid over-generalization: If a lambda is only used for one type, explicit types are clearer and produce better compile-time error messages.
- Generic lambdas cannot be virtual: Closure types are unique anonymous types; their
operator()template cannot be declaredvirtual. - When using with
std::invoke/std::function:std::functionrequires a fixed signature; generic lambdas cannot be directly stored instd::functionunless specific template arguments are provided. - C++20 simplification: C++20 allows
autoas a regular function parameter (abbreviated function template), narrowing the syntactic advantage of generic lambdas, but in C++14/17 they remain the only approach.