C++ Tutorial
C++ Lambdas
A lambda is a function you write inline: [captures](args) { body }.
A function with no name
A lambda is a function you write at the point of use. You do not declare a return type on the left. You do not pick a name unless you store it. The compiler builds a tiny object that can be called like a function.
The shape is always the same: a capture list in square brackets, a parameter list in parentheses, then a brace body. In prose that looks like [captures](args) { body }. StudyGrid compiles these as C++17.
Empty brackets capture nothing
[] means the lambda cannot see local variables from the surrounding function. It only uses its parameters and anything global. Store the lambda in auto: each lambda has its own type, andauto is how you hold it without spelling that type.
Example
#include <iostream>
using namespace std;
int main() {
auto bump = [](int n) {
return n + 10;
};
cout << bump(5) << endl;
cout << bump(20) << endl;
return 0;
}Output is 15 then 30. bump(5) is an ordinary call. The lambda is not a string and not a macro; it runs when you invoke it.
[=] copies, [&] refers
Locals from the enclosing function are hidden unless you capture them. [=] copies every local the body uses. [&] takes those locals by reference, so assignments inside the lambda change the original. Capture only what you need. A named capture such as [rate] or [&total]is clearer than grabbing everything.
Example
#include <iostream>
using namespace std;
int main() {
int rate = 3;
int total = 0;
auto scale = [=](int n) {
return n * rate;
};
auto add = [&](int n) {
total += n;
};
cout << scale(4) << endl;
add(scale(4));
add(5);
cout << total << endl;
return 0;
}scale sees a copy of rate, so it still works if you later change rate.add writes through a reference, so total becomes 17. Do not return a lambda that captured a local by reference after that local is gone.
sort with a comparator lambda
sort can take a third argument: a function that returns whether the first value should come before the second. A lambda is the usual way to write that comparison at the call site. The algorithms chapter used the default order. This one chooses a different order.
Example
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> scores = {6, 1, 9, 4};
sort(scores.begin(), scores.end(), [](int a, int b) {
return a > b;
});
for (int n : scores) {
cout << n << " ";
}
cout << endl;
return 0;
}Output is 9 6 4 1. a > b means larger values come first. The defaultsort(scores.begin(), scores.end()) would have printed 1 4 6 9.
Compare more than numbers
The comparator can look at a field or a property. This program orders words by length, then prints them. Equal lengths keep a stable relative order only if you use stable_sort; ordinary sort may reorder ties.
Example
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<string> plants = {"fig", "apple", "pea", "mango"};
sort(plants.begin(), plants.end(), [](const string& a, const string& b) {
return a.size() < b.size();
});
for (const string& name : plants) {
cout << name << endl;
}
return 0;
}const string& avoids copying each word into the comparator. The body only readssize(). Short names print first: pea, fig, apple, mango — pea and fig are both length 3, and either may appear first.
What the brackets mean
| Capture | Meaning |
|---|---|
[] | Nothing from the outside |
[=] | Copy locals the body uses |
[&] | Refer to locals the body uses |
[rate] | Copy only rate |
[&total] | Refer only to total |
Parameters in the parentheses work like a named function: types, names, pass by value or byconst reference. The body is a block. If every return has the same type, the compiler infers the return type. You can write -> int after the parameters when you want it spelled out.
Keep the body small
A lambda that fills a screen belongs in a named function instead. Use a lambda when the work is a few lines and the name would be used once: a sort order, a small transform, a test for count_if later.
Compile these at /cpp/try. Next: chrono, for measuring how long a piece of code takes, without printing calendar dates.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.
Maths
Squares of 1, 2, 3, 4
A lambda is a function you write at the call site. n * n for each n is 1 4 9 16. for_each runs it. The capture list is empty because the formula needs nothing from outside.
n ↦ n²
Example
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4};
for_each(nums.begin(), nums.end(), [](int n) { cout << n * n << " "; });
cout << endl;
return 0;
}Physics
How many readings exceed 19 °C
count_if with a lambda is a filter. Two of the five samples are at or above 19.0. The threshold is a classroom line, not a weather alert.
Example
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<double> celsius = {18.2, 18.5, 19.0, 18.8, 19.1};
int n = count_if(celsius.begin(), celsius.end(), [](double t) { return t >= 19.0; });
cout << n << " at or above 19 C" << endl;
return 0;
}