C++ Generic Lambdas — auto Parameters and Template Lambdas
이 글의 핵심
This guide covers plain vs generic lambdas, how C++14 auto parameters turn operator() into a function template, C++20 template lambdas, practical STL patterns, type deduction rules, and performance considerations in one place.
What is a generic lambda?
A generic lambda is a C++14 feature: you put auto on a parameter so the same body can be reused for several types. Internally, the closure type’s operator() is a function template.
auto add = [](auto a, auto b) { return a + b; };
int x = add(1, 2); // int
double y = add(1.5, 2.5); // double
Why use it?
- Less duplication: you do not need separate lambdas for
intanddouble. - STL-friendly: easy to pass one comparison or predicate into
std::sort,std::find_if, and similar. - C++20 onward: pairs naturally with the more explicit template lambda syntax.
Reading [Lambda basics](/en/blog/cpp-series-10-1-lambda-basics/ and [auto type deduction](/en/blog/cpp-auto-type-deduction/ first will make this article easier to follow.
Plain lambda vs generic lambda
Plain lambda (non-template operator())
If parameter types are fixed, the closure’s operator() is a single ordinary member function.
auto cmp = [](int a, int b) { return a < b; };
// Conceptually: struct __lambda { bool operator()(int a, int b) const; };
It fits one type; to use double or std::string you would need another lambda.
Generic lambda (auto parameters)
Using auto (or auto&, const auto&, etc.) on a parameter makes operator() a function template.
auto cmp = [](auto a, auto b) { return a < b; };
// Conceptually: struct __lambda {
// template<typename T, typename U>
// auto operator()(T a, U b) const { return a < b; }
// };
The same lambda object can sort and compare int, double, and user-defined types that define operator<.
| Aspect | Plain lambda | Generic lambda |
|---|---|---|
| Parameters | Concrete types | auto / decltype(auto), etc. |
operator() | Non-template | Function template |
| Instances | One | Specializations as needed per call |
| Readability | Clear when simple | Signals “multiple types allowed” |
How auto parameters work
The C++14 rules boil down to this:
- For each
autoparameter in a generic lambda, a distinct template type parameter is introduced. - If the lambda’s return type is not specified, return type deduction follows rules similar to
decltype(auto)from the body’sreturnstatements (same idea as for ordinary lambdas).
[](auto x) { return x * 2; } // A template instance per type of x
[](auto& x) { return x; } // Reference: no copy for read/write
Caveat: plain auto is by value. For large objects, prefer const auto& or auto&& (forwarding-reference-like deduction). That lines up with [perfect forwarding](/en/blog/cpp-perfect-forwarding/.
// Runnable example
std::vector<std::string> v = {"a", "b"};
std::for_each(v.begin(), v.end(), [](const auto& s) {
std::cout << s << '\n'; // Avoid unnecessary copies
});
The compiler’s closure type is implementation-specific, but the key point is that operator() is a template. Template instantiations appear per call pattern, and like ordinary function templates they can be duplicated across TUs that are not merged—same trade-offs as templates generally.
C++20 template lambdas
C++20 lets you express the same idea with explicit template syntax.
auto f = []<typename T>(T x) { return x + x; }; // One type parameter
auto g = []<class T, class U>(T a, U b) { return a < b; };
Differences and benefits
- Named type parameters:
Tis visible in the signature. - Easier to attach
requiresconstraints (C++20 concepts) directly on the lambda.
auto clamp_positive = []<typename T>(T x) requires std::is_arithmetic_v<T> {
return x > T{0} ? x : T{0};
};
With auto parameters only, constraining the anonymous template parameters is awkward; template lambdas pair well with requires. For syntax details see [C++ template lambdas](/en/blog/cpp-template-lambda/ and [generic lambdas and error messages](/en/blog/cpp-error-17-lambda-capture/.
Practical use: STL algorithms
Sorting and comparison
std::vector<std::pair<int, std::string>> items = {{2, "b"}, {1, "a"}};
std::sort(items.begin(), items.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
You do not need to spell out the pair type for a sort on the first element.
Predicate search
auto it = std::find_if(vec.begin(), vec.end(),
[](const auto& e) { return e.id == target_id; });
e is deduced as the container’s value_type.
Transform and accumulate
std::transform(a.begin(), a.end(), out.begin(),
[](auto x) { return std::abs(x); });
int sum = std::accumulate(v.begin(), v.end(), 0,
[](auto acc, const auto& x) { return acc + x.size(); });
std::visit and variants
std::visit([](const auto& x) { std::cout << x; }, my_variant);
One lambda body can handle each alternative of std::variant (a distinct operator() instantiation per alternative).
Type deduction rules
- Parameter
auto: Same family as [autoin a function template parameter](/en/blog/cpp-auto-type-deduction/—template arguments are deduced from the arguments at the call site. auto&/const auto&: Express intent to preserve lvalue-ness and constness.const auto&also binds to temporaries and is widely applicable.auto&&: [Forwarding reference](/en/blog/cpp-perfect-forwarding/ rules apply, so you can bind lvalues and rvalues efficiently (typical for “perfect forwarding” inside a generic lambda).- Return type: If omitted, all
returnpaths must deduce to a compatible type. Different types in branches (e.g.intvsdouble) cause a deduction conflict.
// Likely error: int vs double in different branches
// [](auto x) { return cond ? 0 : 1.0; } // Deduction clash
When needed, give the lambda an explicit return type:
[](auto x) -> double { return x * 1.0; }
Performance considerations
- Overhead: A lambda call is like a small inlineable function. Generics are still not virtual (standard lambdas are not polymorphic through vtables).
- Code size: Each distinct argument-type combination can produce another template instance. Hundreds of distinct signatures can bloat the binary; sometimes a non-template function or a single-type lambda is better.
- Capture:
[=],[&], etc.—captured state affects closure size whether or not the lambda is generic. - Optimization: Compilers often inline comparison lambdas for
std::sortaggressively. Do not assume “generic means slow”—measure before optimizing.
decltype and generic lambdas
For a parameter named x, decltype(x) reflects the actual argument type at the call. That is handy when you need a local “same type as x” in the body.
// Variable declaration and initialization
auto f = [](auto x) -> decltype(x) {
decltype(x) copy = x;
return copy;
};
Together with the [decltype guide](/en/blog/cpp-decltype/, this also clarifies differences from lambdas that use decltype(auto) as the return type.
constexpr generic lambdas (C++17)
If a lambda is constexpr and the body satisfies constexpr requirements, it can be evaluated at compile time. Generic lambdas behave the same: when arguments are literals (or otherwise constexpr-friendly), the corresponding template instance can be evaluated as a constexpr call.
constexpr auto sq = [](auto x) { return x * x; };
static_assert(sq(3) == 9);
Using non-literal-friendly types such as std::string as arguments usually forces runtime evaluation. See [constexpr lambdas](/en/blog/cpp-constexpr-lambda/.
Common mistakes
- Only
autoby value but you meant to mutate elements: You modify a copy; container elements stay unchanged. Useauto&orauto*as appropriate. - Two different
autoparameters:auto a, auto bare two independent template parameters. To force the same type, a C++20 template lambda withtemplate<typename T> ... (T a, T b)is clearer. - Recursion: A nameless lambda is awkward to call from inside itself. Consider
std::function, a Y combinator pattern, or a named function object.
Summary
| Keyword | Meaning |
|---|---|
| Generic lambda | auto parameters → templated operator() |
| C++20 | Explicit template lambdas: []<typename T>(T x){} |
| STL | Less type repetition in sort, search, transform, visit |
| Performance | Inline, non-virtual; instance count scales with types used |
See also: Lambda capture, [constexpr lambdas](/en/blog/cpp-constexpr-lambda/, [decltype](/en/blog/cpp-decltype/.
Related posts on this site
- [C++ template lambdas](/en/blog/cpp-template-lambda/
- C++ lambda capture
- [C++ auto type deduction](/en/blog/cpp-auto-type-deduction/
More reading
- [C++ lambdas — complete guide](/en/blog/cpp-lambda-complete/
- [Modern C++ (C++11–C++20) syntax cheat sheet](/en/blog/cpp-cheatsheet-02-modern-cpp-syntax/
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Generic lambdas in C++14: auto makes operator() a template, C++20 template lambdas, STL patterns, deduction rules, perfo… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [C++ Lambda Expressions | · capture](/en/blog/cpp-lambda-expressions/
- [C++ Lambdas: Syntax, Captures, mutable, and Generic Lambdas](/en/blog/cpp-lambda-complete/
- C++ auto Type Deduction | Let the Compiler Handle Complex Types
이 글에서 다루는 키워드 (관련 검색어)
C++, generic lambda, lambda, C++14, C++20, template 등으로 검색하시면 이 글이 도움이 됩니다.