C++ Lambdas: Syntax, Captures, mutable, and Generic Lambdas
이 글의 핵심
Hands-on guide to C++ lambdas—syntax, captures, mutable, and patterns for algorithms and callbacks.
Basic grammar
Lambdas are useful when you want to put short function logic in one place for STL algorithms and asynchronous callbacks.In this article, you can learn how to select the capture list and return type by following the grammar and capture examples in the following sections in order.
// Basic form
auto lambda = [] {
cout << "Hello Lambda!" << endl;
};
lambda(); // call
// Parameters and return type
auto add = [](int a, int b) -> int {
return a + b;
};
cout << add(3, 5) << endl; // 8
// Deduced return type
auto multiply = [](int a, int b) {
return a * b; // int
};
Capture
Value Capture
int x = 10;
int y = 20;
// capture by value
auto lambda1 = [x, y]() {
cout << x + y << endl;
};
// Capture all variables by value
auto lambda2 = [=]() {
cout << x + y << endl;
};
Reference Capture
int count = 0;
// capture by reference
auto increment = [&count]() {
count++;
};
increment();
cout << count << endl; // 1
// Capture all variables by reference
auto lambda = [&]() {
count++;
};
Mixed Capture
int x = 10;
int y = 20;
// x is the value, y is the reference
auto lambda = [x, &y]() {
y = x + 5;
};
lambda();
cout << y << endl;// 15
mutable lambda
int x = 10;
// value capture is const by default
auto lambda1 = [x]() {
//x++;// error!const
};
// Can be modified with mutable
auto lambda2 = [x]() mutable {
x++;// OK (edit copy)
cout << x << endl;
};
lambda2();// 11
cout << x << endl;// 10 (original unchanged)
Practical example
Example 1: STL algorithm and lambda
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Keep only evens
auto it = remove_if(numbers.begin(), numbers.end(), [](int x) {
return x % 2 != 0;
});
numbers.erase(it, numbers.end());
// Print
for_each(numbers.begin(), numbers.end(), [](int x) {
cout << x << " ";
});
cout << endl; // 2 4 6 8 10
// Sort descending
sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b;
});
// Check predicate
bool allEven = all_of(numbers.begin(), numbers.end(), [](int x) {
return x % 2 == 0;
});
cout << "all even: " << allEven << endl;
return 0;
}
Description: Lambdas allow you to write simple logic inline without a separate function.
Example 2: Event handler
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class Button {
private:
string label;
function<void()> onClick;
public:
Button(string l) : label(l) {}
void setOnClick(function<void()> handler) {
onClick = handler;
}
void click() {
cout << label << "Click!"<< endl;
if (onClick) {
onClick();
}
}
};
int main() {
int clickCount = 0;
Button btn1("Button1");
btn1.setOnClick([&clickCount]() {
clickCount++;
cout << "Click count: " << clickCount << endl;
});
Button btn2("Button2");
btn2.setOnClick([]{
cout << "Button 2 was clicked!" << endl;
});
btn1.click();
btn1.click();
btn2.click();
return 0;
}
Description: You can register event handlers succinctly with lambdas.
Example 3: Generic lambda (C++14)
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
// Generic lambda (auto parameters)
auto print = [](auto x) {
cout << x << endl;
};
print(10); // int
print(3.14); // double
print("Hello"); // const char*
auto add = [](auto a, auto b) {
return a + b;
};
cout << add(1, 2) << endl;
cout << add(1.5, 2.5) << endl;
cout << add(string("Hello"), string(" World")) << endl;
auto printVector = [](const auto& vec) {
for (const auto& item : vec) {
cout << item << " ";
}
cout << endl;
};
vector<int> v1 = {1, 2, 3};
vector<string> v2 = {"a", "b", "c"};
printVector(v1);// 1 2 3
printVector(v2);// a b c
return 0;
}
Explanation: Starting with C++14, you can create a generic lambda with an auto parameter.
Frequently occurring problems
Problem 1: Dangling References
Symptom: Crash or strange values when running lambda
Cause: The reference-captured variable has already been destroyed.
Fix:
// ❌ Dangerous code
function<void()> makeCounter() {
int count = 0;
return [&count]() { // See dangling!
count++;
cout << count << endl;
};
}
auto counter = makeCounter();
counter();// crash or strange value
// ✅ Capture by value
function<void()> makeCounter() {
int count = 0;
return [count]() mutable {
count++;
cout << count << endl;
};
}
Problem 2: Capturing this
Symptom: Inability to access member variables
Cause: Missing capture of this
Solution:
class Counter {
private:
int count = 0;
public:
void increment() {
// ❌ Error: cannot reference members without capture
// auto bad = [] { count++; };
// ✅ Capture this
auto lambda2 = [this]() {
count++;
};
// ✅ C++17: copy *this
auto lambda3 = [*this]() mutable {
count++; // modifies copy
};
}
};
Issue 3: Capture initialization (C++14)
Symptom: Complex initialization is not possible
Cause: Only simple capture is possible
Solution:
// ❌ Not possible in C++11
unique_ptr<int> ptr = make_unique<int>(10);
auto lambda = [ptr]() { // Error!Unable to copy unique_ptr
cout << *ptr << endl;
};
// ✅ C++14: Capture initialization
unique_ptr<int> ptr = make_unique<int>(10);
auto lambda = [ptr = move(ptr)]() {
cout << *ptr << endl;
};
FAQ
Q1: When do you use lambda?
A:
- Predicates of STL algorithm
- Event handler
- Callback function
- Simple one-off function
Q2: Lambda vs function pointer?
A: Lambdas are more flexible and captureable.Function pointers are converted only to lambdas without capture.
Q3: What is the type of lambda?
A: Each lambda has a unique type.Save it as auto or function<>.
Q4: What is a recursive lambda?
A: Available starting from C++14.
function<int(int)> factorial = [&factorial](int n) {
return n <= 1 ?1 : n * factorial(n - 1);
};
Q5: Does lambda affect performance?
A: When inlined, there is very little overhead.Same performance as a regular function.
Q6: What are the capture defaults?
A:
[=]: all as values (not recommended, capture explicitly)[&]: all by reference (Caution: risk of dangling references)[]: No capture (most secure)
Good article to read together (internal link)
Here’s another article related to this topic.
- C++ Lambda Capture |“Lambda Capture” complete guide
- Complete guide to C++ lambda basics |Capture·mutable·generic lambda and practical patterns
- C++ Init Capture |“Init Capture” Guide
Practical tips
These are tips that can be applied right away in practice.
Debugging tips
- If you run into a problem, check the compiler warnings first.
- Reproduce the problem with a simple test case
Performance Tips
- Don’t optimize without profiling
- Set measurable indicators first
Code review tips
- Check in advance for areas that are frequently pointed out in code reviews.
- Follow your team’s coding conventions
Practical checklist
This is what you need to check when applying this concept in practice.
Before writing code
- Is this technique the best way to solve the current problem?
- Can team members understand and maintain this code?
- Does it meet the performance requirements?
Writing code
- Have you resolved all compiler warnings?
- Have you considered edge cases?
- Is error handling appropriate?
When reviewing code
- Is the intention of the code clear?
- Are there enough test cases?
- Is it documented?
Use this checklist to reduce mistakes and improve code quality.
Keywords covered in this article (related search terms)
This article will be helpful if you search for C++, lambda, lambda, anonymous function, C++11, etc.
Related articles
- C++ std::function vs function pointer |
- C++ lambda capture error |
- C++ Lambda Capture |
- C++ async & launch |
- C++ Atomic Operations |