본문으로 건너뛰기
Previous
Next
C++ Generic Lambdas — auto Parameters and Template Lambdas

C++ Generic Lambdas — auto Parameters and Template Lambdas

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 int and double.
  • 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<.

AspectPlain lambdaGeneric lambda
ParametersConcrete typesauto / decltype(auto), etc.
operator()Non-templateFunction template
InstancesOneSpecializations as needed per call
ReadabilityClear when simpleSignals “multiple types allowed”

How auto parameters work

The C++14 rules boil down to this:

  1. For each auto parameter in a generic lambda, a distinct template type parameter is introduced.
  2. If the lambda’s return type is not specified, return type deduction follows rules similar to decltype(auto) from the body’s return statements (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: T is visible in the signature.
  • Easier to attach requires constraints (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.

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

  1. Parameter auto: Same family as [auto in a function template parameter](/en/blog/cpp-auto-type-deduction/—template arguments are deduced from the arguments at the call site.
  2. auto& / const auto&: Express intent to preserve lvalue-ness and constness. const auto& also binds to temporaries and is widely applicable.
  3. 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).
  4. Return type: If omitted, all return paths must deduce to a compatible type. Different types in branches (e.g. int vs double) 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::sort aggressively. 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

  1. Only auto by value but you meant to mutate elements: You modify a copy; container elements stay unchanged. Use auto& or auto* as appropriate.
  2. Two different auto parameters: auto a, auto b are two independent template parameters. To force the same type, a C++20 template lambda with template<typename T> ... (T a, T b) is clearer.
  3. Recursion: A nameless lambda is awkward to call from inside itself. Consider std::function, a Y combinator pattern, or a named function object.

Summary

KeywordMeaning
Generic lambdaauto parameters → templated operator()
C++20Explicit template lambdas: []<typename T>(T x){}
STLLess type repetition in sort, search, transform, visit
PerformanceInline, non-virtual; instance count scales with types used

See also: Lambda capture, [constexpr lambdas](/en/blog/cpp-constexpr-lambda/, [decltype](/en/blog/cpp-decltype/.


  • [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++, generic lambda, lambda, C++14, C++20, template 등으로 검색하시면 이 글이 도움이 됩니다.