What a pair is
Some values belong together: a pet has a name and an age, a point has an x and a y. A pair holds exactly two values in one variable. The two types go in angle brackets, in order, and the values go in curly braces:
pair<string, int> pet = {"Rex", 4};
cout << pet.first << endl; // Rex
cout << pet.second << endl; // 4
.first is the first value and .second is the second. They are members, not functions, so there are no parentheses. The two types can differ, as here, or be the same, as in pair<int, int>.
Unpacking a pair
Instead of writing pet.first and pet.second every time, you can give the two values their own names:
auto [name, age] = pet;
cout << name << " " << age << endl; // Rex 4
auto [name, age] creates two new variables. The names are matched to the pair's values in order: the first name gets first, the second gets second. The types come from the pair, so name is a string and age is an int. The same square-bracket form works in a range-based loop, as in for (auto& [name, points] : scores).
Returning a pair from a function
A function that has two results can return one pair. Its return type is the pair's type, and return {a, b}; builds the pair:
pair<int, int> splitTime(int minutes) {
return {minutes / 60, minutes % 60};
}
With 135 minutes, 135 / 60 is the whole hours, 2, and 135 % 60 is what is left, 15. The caller can unpack the result straight away: auto [hours, mins] = splitTime(135);.
Pairs in other places
Pairs show up all over C++. A vector<pair<string, int>> is a list of records like (name, age), and a loop over it can unpack every element at once with for (auto& [name, age] : pets). A map is a collection of pairs too: each entry has a key and a value, which are the first and the second of the entry. Once .first and .second feel natural, those loops read the same way.
Gotchas
- A pair can't be printed directly.
cout << petdoesn't compile: the compiler reports that there is nooperator<<forstd::ostreamandstd::pair<std::string, int>. Printpet.firstandpet.secondinstead. - Order matters.
auto [age, name] = pet;compiles, butagewould hold the string andnamethe number. Names follow the order of the pair's types. - Unpacking copies.
auto [name, age] = pet;makes new variables holding copies of the values, so changingagelater does not changepet.second. Each name can only be declared once in a scope.
The exercise, step by step
- Make
splitTimereturn its pair. Return the whole hours and the minutes left in curly braces. - Unpack
petintonameandagewithautoand square brackets. - Unpack the result of
splitTime(135)intohoursandmins. A returned pair unpacks just like a variable.
The finished lesson code:
class Solution {
public:
pair<int, int> splitTime(int minutes) {
return {minutes / 60, minutes % 60};
}
void mainFunction() {
pair<string, int> pet = {"Rex", 4};
cout << pet.first << endl;
auto [name, age] = pet;
cout << name << " is " << age << endl;
auto [hours, mins] = splitTime(135);
cout << name << " walked " << hours << " h " << mins << " min" << endl;
}
};
It prints:
Rex
Rex is 4
Rex walked 2 h 15 min
Why not just two variables?
Two separate variables work fine inside one function, but a pair travels as one value. A function can return it, a vector can hold many of them, and the two parts can never get out of step, such as a name that ends up with the wrong age.
Key takeaways
pair<A, B> p = {x, y};keeps two values together; read them withp.firstandp.second.auto [a, b] = p;unpacks a pair into two new variables, in order.- A function returns two results as a pair with
return {x, y};. - Print the two parts separately;
coutdoesn't know how to print a whole pair.
