C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
86: Generic Lambdas
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_sumthat takes astd::vector<T>as its argument. - Use C++20 concepts or
std::is_arithmetic_vwithin arequiresclause 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 ofdouble, and one offloat. - 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.
There are no comments for now.