std::tuple / std::pair
In C++, std::tuple and std::pair are template classes for grouping multiple values together. pair holds exactly two values, while tuple can hold any number of values. They are useful when you want to return multiple values from a function or bundle values of different types into a single object.
Syntax
// ======================================== // std::pair basic syntax // ======================================== #include <utility> // required to use pair // Create a pair (explicit type version) std::pair<Type1, Type2> varName = std::make_pair(val1, val2); // Access pair elements varName.first // access the first value varName.second // access the second value // ======================================== // std::tuple basic syntax // ======================================== #include <tuple> // required to use tuple // Create a tuple std::tuple<Type1, Type2, Type3> varName = std::make_tuple(val1, val2, val3); // Access tuple elements (index must be a compile-time constant) std::get<0>(varName) // access element at index 0 std::get<1>(varName) // access element at index 1 std::get<2>(varName) // access element at index 2 // Decompose (unpack) using tie() std::tie(varA, varB, varC) = tupleVar; // Use std::ignore to skip unwanted elements std::tie(varA, std::ignore, varC) = tupleVar;
Syntax Reference
| Syntax / Function | Description |
|---|---|
| std::pair<T1, T2> | A template class that groups two values. Access via .first and .second. |
| std::make_pair(a, b) | A helper function that creates a pair concisely using type deduction. |
| std::tuple<T1, T2, ...> | A template class that groups any number of values of any types. |
| std::make_tuple(a, b, ...) | A helper function that creates a tuple concisely using type deduction. |
| std::get<N>(t) | Retrieves the N-th element (0-based) of a tuple or pair. N must be a compile-time constant. |
| std::tie(a, b, ...) | Creates a tuple of references to variables and decomposes (unpacks) a tuple on the right side into each variable. |
| std::ignore | A placeholder used with std::tie() to skip unwanted elements. |
| std::tuple_size<T>::value | Retrieves the number of elements in a tuple at compile time. |
Sample Code
sg_labmem_data.cpp
// ========================================
// sg_labmem_data.cpp
// Manages Steins;Gate lab members using tuple/pair,
// bundling name, role, lab-member number,
// and time-leap count together.
// ========================================
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
// ========================================
// Returns name and lab-member number
// bundled together as a pair
// ========================================
std::pair<std::string, int> getLabMemberBasic(int index) {
// pair of lab-member name and number
std::pair<std::string, int> members[5];
members[0] = std::make_pair("Okabe Rintaro", 1);
members[1] = std::make_pair("Shiina Mayuri", 2);
members[2] = std::make_pair("Hashida Itaru", 3);
members[3] = std::make_pair("Makise Kurisu", 4);
members[4] = std::make_pair("Amane Suzuha", 8);
if (index >= 0 && index < 5) {
return members[index];
}
return std::make_pair("Unknown", 0);
}
// ========================================
// Returns four pieces of information—name, role,
// lab-member number, and time-leap count—bundled in a tuple
// ========================================
std::tuple<std::string, std::string, int, int> getLabMemberDetail(const std::string& name) {
// name / role / lab-member number / time-leap count
if (name == "Okabe Rintaro") {
return std::make_tuple("Okabe Rintaro", "Lab leader, inventor", 1, 3);
} else if (name == "Makise Kurisu") {
return std::make_tuple("Makise Kurisu", "Theoretical physics", 4, 0);
} else if (name == "Amane Suzuha") {
return std::make_tuple("Amane Suzuha", "Time-machine maintenance", 8, 1);
}
return std::make_tuple("Unknown", "Unknown", 0, 0);
}
int main() {
std::cout << "=== Future Gadget Lab Member Information System ===" << std::endl << std::endl;
// ----------------------------------------
// Basic usage of pair
// ----------------------------------------
std::cout << "--- pair: lab-member name and number ---" << std::endl;
std::pair<std::string, int> m0 = getLabMemberBasic(0);
std::pair<std::string, int> m3 = getLabMemberBasic(3);
// use .first for name and .second for number
std::cout << "Member: " << m0.first << " Number: " << m0.second << std::endl;
std::cout << "Member: " << m3.first << " Number: " << m3.second << std::endl;
std::cout << std::endl;
// ----------------------------------------
// Storing pairs in a vector
// ----------------------------------------
std::cout << "--- pair vector: divergence meter readings ---" << std::endl;
std::vector<std::pair<std::string, double> > divergenceLog;
divergenceLog.push_back(std::make_pair("Alpha world line", 1.048596));
divergenceLog.push_back(std::make_pair("Beta world line", 0.571024));
divergenceLog.push_back(std::make_pair("Steins Gate", 1.048596));
for (int i = 0; i < (int)divergenceLog.size(); i++) {
std::cout << divergenceLog[i].first << " divergence: " << divergenceLog[i].second << std::endl;
}
std::cout << std::endl;
// ----------------------------------------
// Using tuple to bundle four values
// ----------------------------------------
std::cout << "--- tuple: detailed member information ---" << std::endl;
std::tuple<std::string, std::string, int, int> detail = getLabMemberDetail("Okabe Rintaro");
// access each element with std::get<index>()
std::cout << "Name: " << std::get<0>(detail) << std::endl;
std::cout << "Role: " << std::get<1>(detail) << std::endl;
std::cout << "Member number: " << std::get<2>(detail) << std::endl;
std::cout << "Time-leap count: " << std::get<3>(detail) << std::endl;
std::cout << std::endl;
// ----------------------------------------
// Unpacking a tuple with std::tie()
// ----------------------------------------
std::cout << "--- tie(): unpacking a tuple ---" << std::endl;
std::string memberName, role;
int labNumber, leapCount;
// assign all tuple values to variables at once with tie()
std::tie(memberName, role, labNumber, leapCount) = getLabMemberDetail("Makise Kurisu");
std::cout << memberName << " / " << role << " / Member " << labNumber
<< " / Time leaps: " << leapCount << std::endl;
std::cout << std::endl;
// ----------------------------------------
// Skipping unwanted elements with std::ignore
// ----------------------------------------
std::cout << "--- ignore: skip unwanted elements ---" << std::endl;
std::string targetName;
int targetLeapCount;
// role and member number are not needed, so use std::ignore
std::tie(targetName, std::ignore, std::ignore, targetLeapCount) = getLabMemberDetail("Amane Suzuha");
std::cout << targetName << " Time-leap count: " << targetLeapCount << std::endl;
if (targetLeapCount > 0) {
std::cout << " -> A time traveler from the future." << std::endl;
}
return 0;
}
# Compile g++ -std=c++11 sg_labmem_data.cpp -o sg_labmem_data # Run ./sg_labmem_data === Future Gadget Lab Member Information System === --- pair: lab-member name and number --- Member: Okabe Rintaro Number: 1 Member: Makise Kurisu Number: 4 --- pair vector: divergence meter readings --- Alpha world line divergence: 1.04860 Beta world line divergence: 0.57102 Steins Gate divergence: 1.04860 --- tuple: detailed member information --- Name: Okabe Rintaro Role: Lab leader, inventor Member number: 1 Time-leap count: 3 --- tie(): unpacking a tuple --- Makise Kurisu / Theoretical physics / Member 4 / Time leaps: 0 --- ignore: skip unwanted elements --- Amane Suzuha Time-leap count: 1 -> A time traveler from the future.
Common Mistakes
When using std::tuple and std::pair, watch out for out-of-range index access with get<> and type-deduction surprises in structured bindings.
Out-of-range index in get<>
If N in std::get<N>(t) is equal to or greater than the number of elements in the tuple, it is a compile error. This can be caught at compile time rather than at runtime, but be careful not to accidentally access the wrong element due to a type mismatch. Use std::tuple_size<T>::value to check the element count.
NG: out-of-range index (compile error)
#include <tuple>
#include <string>
int main() {
// tuple with 3 elements (valid indices are 0, 1, and 2 only)
std::tuple<std::string, int, double> t("Okabe Rintaro", 1, 1.048596);
// NG: index 3 is equal to the element count (3), so this is a compile error
std::get<3>(t);
return 0;
}
OK: valid indices and checking with tuple_size
#include <iostream>
#include <tuple>
int main() {
std::tuple<std::string, int, double> t("Okabe Rintaro", 1, 1.048596);
std::cout << std::get<0>(t) << std::endl; // Okabe Rintaro
std::cout << std::get<1>(t) << std::endl; // 1
std::cout << std::get<2>(t) << std::endl; // 1.048596
// use tuple_size to check the element count
constexpr int size = std::tuple_size<decltype(t)>::value;
std::cout << "element count: " << size << std::endl; // 3
return 0;
}
Type-deduction mistake in structured bindings (C++17)
C++17 structured bindings (auto [a, b, c] = t;) are convenient, but they copy by default. If you want changes to be reflected back in the original tuple, you must use a reference: auto& [a, b, c] = t;. Using const auto& makes it read-only.
NG: value copy does not update the original tuple
#include <iostream>
#include <tuple>
int main() {
std::tuple<std::string, int> labMem("Shiina Mayuri", 2);
// NG: received as a value copy, so the change is not reflected in the original tuple
auto [name1, num1] = labMem;
num1 = 99; // labMem is not changed
std::cout << "After copy (original value): " << std::get<1>(labMem) << std::endl; // still 2
return 0;
}
OK: reference binding reflects changes in the original
#include <iostream>
#include <tuple>
int main() {
std::tuple<std::string, int> labMem("Shiina Mayuri", 2);
// OK: received as a reference, so the change is reflected
auto& [name2, num2] = labMem;
num2 = 99; // labMem is updated
std::cout << "After reference (updated): " << std::get<1>(labMem) << std::endl; // 99
return 0;
}
Overview
std::pair requires the <utility> header and std::tuple requires the <tuple> header. pair is specialized for two elements and provides intuitively named access via .first and .second, making it a natural fit for map key-value pairs and returning two values from a function. tuple is useful when three or more elements are needed; access individual elements with std::get<N>(). When returning multiple values, std::tie() makes unpacking concise. Unwanted elements can be skipped with std::ignore. Note that in C++03, declaring a vector of pairs required a space between the closing angle brackets (vector<pair<T1, T2> >), but since C++11 >> is fine as well. When the number of elements or types becomes large and the code becomes hard to read, consider defining a struct or class with named member variables instead.
If you find any errors or copyright issues, please contact us.