Sometimes a function or container needs to keep two related values together.
You could write your own small struct, but there’s no need to reinvent the wheel.
C++ already provides a simple standard option: std::pair. It is a class template from <utility> that stores exactly two values, named first and second.
Three things to take away:
std::pair<T, U>stores two values, possibly of different types.- The two members are named
firstandsecond. - Use
std::pairfor small, obvious two-value groupings; use a custom struct when names matter.
The basic idea
std::pair is a class template.
This means the member types are supplied in angle brackets:
std::pair<int, double>
That pair stores:
first → int
second → double
Example:
#include <iostream>
#include <utility>
int main(){
std::pair<int, double> sensorReading { 7, 24.6 };
std::cout << "Sensor ID: " << sensorReading.first << '\n';
std::cout << "Temperature: " << sensorReading.second << " C\n";
}
Terminal output:
Sensor ID: 7
Temperature: 24.6 C
The pair contains two members:
sensorReading.first
sensorReading.second
Heterogeneous values
std::pair can store two different types.
#include <iostream>
#include <string>
#include <utility>
int main(){
std::pair<std::string, int> playerScore { "Ada", 900 };
std::cout << playerScore.first << " scored "
<< playerScore.second << " points\n";
}
Terminal output:
Ada scored 900 points
Here, the first value is a std::string.
The second value is an int.
That is why std::pair is called a small heterogeneous container: it can hold two values of different types.
Class template argument deduction (CTAD) C++17
CTAD is compatible with std::pair.
#include <iostream>
#include <string>
#include <utility>
int main(){
std::pair playerScore { std::string { "Ada" }, 900 };
std::cout << playerScore.first << " scored "
<< playerScore.second << " points\n";
}
Terminal output:
Ada scored 900 points
The compiler deduces:
std::pair<std::string, int>
Returning two related values
std::pair is useful when a function naturally returns two values and the relationship is obvious.
For example, splitting a score into wins and losses:
#include <iostream>
#include <utility>
std::pair<int, int> seasonRecord(){
return { 14, 6 };
}
int main(){
std::pair<int, int> record { seasonRecord() };
std::cout << "Wins: " << record.first << '\n';
std::cout << "Losses: " << record.second << '\n';
}
Terminal output:
Wins: 14
Losses: 6
This is compact. However, once the values are stored in the members, the semantics are lost
record.first // What does this mean?
record.second
std::pair in the Standard Library
std::pair appears throughout the Standard Library.
A major example is std::map.
Each element in a std::map<K, V> behaves like a pair:
std::pair<const K, V>
The first member is the key.
The second member is the mapped value.
#include <iostream>
#include <map>
#include <string>
int main(){
std::map<std::string, int> stock {
{ "resistors", 250 },
{ "capacitors", 120 }
};
for (const auto& item : stock) {
std::cout << item.first << ": "
<< item.second << '\n';
}
}
// Terminal output:
capacitors: 120
resistors: 250
The map stores key-value pairs.
That is why this syntax appears:
item.first
item.second
first is the key.
second is the value.
Comparing pairs
std::pair supports comparison if its stored types support comparison.
Pairs are compared lexicographically: first compare first, then compare second if needed.
#include <iostream>
#include <utility>
int main(){
std::pair<int, int> a { 2, 5 };
std::pair<int, int> b { 2, 9 };
std::cout << std::boolalpha;
std::cout << (a < b) << '\n';
std::cout << (a == b) << '\n';
}
Terminal output:
true
false
The first members are equal:
2 == 2
So the comparison moves to the second members:
5 < 9
Therefore, a < b is true.
When not to use std::pair
std::pair is convenient, but it can become unclear.
This is acceptable for small, obvious groupings:
std::pair<int, int> record { 14, 6 };
But if the meaning matters across the program, prefer a named struct:
struct SeasonRecord {
int wins {};
int losses {};
};
Now the call site is self-documenting:
record.wins
record.losses
That is clearer than:
record.first
record.second
The rule is simple:
Use
std::pairwhen the relationship is obvious and local. Use a struct when the values deserve names.
std::pair vs std::tuple
std::pair stores exactly two values.
std::tuple generalizes this idea to zero or more values:
std::tuple<int, double, char>
Use std::pair when there are exactly two values and first / second is readable enough.
Use a custom struct when member names matter.
Use std::tuple sparingly when the values are positional and the grouping is small. If a tuple starts needing explanation, it probably wants to become a struct.
Takeaway
std::pair is a small, simple standard class template for storing exactly two related values:
It is useful for local two-value results, key-value relationships, and Standard Library interfaces such as std::map. However, the trade-off is a loss in semantics.