Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

When you first encounter the auto keyword in a lambda parameter list, it's easy to fall into a mental trap. I see this constantly: learners assume that a generic lambda is just a convenient way to write a "flexible" function that handles different types at runtime—similar to how std::function or a void pointer might work in C.

"It's just a flexible function pointer" vs. "It's a template in disguise"

Here is why that assumption is wrong. If a generic lambda were just a "flexible" function, the compiler would have to do some heavy lifting at runtime to figure out the types (type erasure). But C++ doesn't do that for lambdas. Instead, a generic lambda is actually a template.

Look at this example. I want a lambda that calculates the difference between two values, regardless of whether they are integers, floats, or doubles:

auto diff = [](auto a, auto b) {
    return a - b;
};

int main() {
    diff(10, 5);       // Call 1
    diff(10.5, 5.2);   // Call 2
}

To the compiler, that diff lambda isn't one function. It's a class (a closure type) with a templated operator(). When you call diff(10, 5), the compiler generates a version of the code specifically for int. When you call diff(10.5, 5.2), it generates a completely separate version for double. This is static polymorphism. It's incredibly fast because there is no runtime overhead, but it means your binary size grows slightly with every new type you use.

When "any type" is too many types

The problem with auto is that it's too permissive. If you pass a std::string into that diff lambda above, the compiler will try to subtract one string from another. You'll get a massive, terrifying template error message that lasts for three pages because std::string doesn't support the - operator.

In a real production codebase, I never leave a generic lambda completely open if I know it should only work for numbers. Since C++20, we can use requires clauses directly in the lambda's parameter list to constrain the types. This is where you move from "it works" to "it's professional software."

#include <concepts>

auto safe_diff = [](std::floating_point auto a, std::floating_point auto b) {
    return a - b;
};

// Or using a more general numeric constraint
auto numeric_diff = [](auto a, auto b) requires std::is_arithmetic_v<decltype(a)> && std::is_arithmetic_v<decltype(b)> {
    return a - b;
};

By adding those constraints, you shift the error from "template instantiation failed" (which is cryptic) to "constraints not satisfied" (which tells you exactly why the type was rejected). I highly recommend this approach; it saves you and your teammates hours of debugging.

Handling mixed types with decltype(auto)

One last thing: what happens if you pass an int and a double? The return type needs to be handled carefully. While auto usually suffices for the return type, using decltype(auto) is a safer bet when you're writing generic wrappers, as it preserves references and cv-qualifiers that a plain auto might strip away.

auto generic_wrapper = [](auto&& arg) -> decltype(auto) {
    // Do something with arg...
    return arg; 
};

It feels like a lot of syntax, but once it clicks, you realize you're essentially writing a template function with the convenience of a local variable.




📋 Practical Task

The Multi-Type Sequence Summary Tool

Your task is to create a tool that can calculate the sum of a collection of numbers, regardless of whether the collection contains int, float, or double.

Requirements:

  • Write a generic lambda named calculate_sum that takes a std::vector<T> as its argument.
  • Use C++20 concepts or std::is_arithmetic_v within a requires clause to ensure the lambda only accepts vectors containing numeric types.
  • The lambda should return the total sum of the elements.
  • Test your lambda with three different vectors: one of int, one of double, and one of float.
  • Try passing a std::vector<std::string> to the lambda and verify that the compiler catches the error at the call site rather than deep inside the lambda's logic.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.