Type Deduction for Tuples
Tuple elements are numbered starting from zero, unlike pair elements that are named first and second.
Predicting a Tuple Element Before Reading On
A tuple holds several values together, and it is often created so that the compiler works out the type of each value for you. Before you learn how to read one of those values back out, it helps to try a small prediction. Imagine a tuple that holds a string, an int and a double, created with make_tuple. Someone then asks for one of its elements using a numbered index. Which value would you expect to come back? Keep your guess in mind, because the rest of this article shows how positions in a tuple work.
What do you think happens?
Given a tuple t holding a string, an int and a double in that order, what does get<1>(t) return?
Reveal answer
The int, because index 1 is the second element when counting starts at zero.
Tuple positions are numbered from zero, so the first element is at index 0, the second at index 1 and the third at index 2. Index 1 therefore selects the int.
Why Tuple Positions Start at Zero
The elements of a tuple are numbered, starting with zero. This is different from a pair, whose two elements are given names: first and second. A pair lets you ask for its first element by name, but a tuple has no names for its elements at all. Position is the only handle you have, so every element must be identified by the number that marks where it sits.
The first element is at index 0, the second at index 1 and the third at index 2. Counting from zero is the rule for every tuple index. This means that the first element is never reached with index 1, even though it may feel like the natural first position to a beginner. Once you accept that the numbering begins at zero, the rest of the access rules follow directly.
The diagram uses a string, an int and a double, the same kinds of values used in the standard example for this topic, with sample values added here for illustration. Reading the boxes from left to right gives the order of the elements. The number under each box is the value you pass to get. Index 1 selects the middle box, which holds the int, not the string.
Creating a Tuple with make_tuple
A tuple is commonly created with make_tuple, which lets the type of the tuple be deduced from the values you give it. You do not write out the type of each element yourself; the compiler works it out from the values. In the code below, the tuple holds a string, an int and a double, in that order.
apple
7
2.5make_tuplebuilds the tuple and deduces each element's type from its value- get<0>(t) reads the element at position 0, the string
- get<1>(t) reads the element at position 1, the int
- get<2>(t) reads the element at position 2, the double
Retrieving Elements with get<index>
A tuple element is accessed with get<index>(tuple). The index is written inside the angle brackets, and the tuple is passed as the argument in the parentheses. Each call returns the element at the position named by the index. Because the index is part of the code itself, the compiler knows which slot is being read before the program runs. This is what the standard approach calls compile-time selection.
Predicting the output of three get calls
A tuple t is built with make_tuple from the string "apple", the int 7 and the double 2.5. Predict what each of the three get calls prints.
- 1Identify the positions
Counting from zero, the string is at index 0, the int at index 1 and the double at index 2.
- 2Evaluate get<0>(t)
Index 0 selects the first element, so the call returns the string apple.
- 3Evaluate get<1>(t)
Index 1 selects the second element, so the call returns the int 7, not the string.
- 4Evaluate get<2>(t)
Index 2 selects the third element, so the call returns the double 2.5.
The program prints apple, then 7, then 2.5.
Why get<index> Instead of get(t,1) or t[1]
A common first guess when learning tuples is that the elements can be reached the same way as any other collection: by writing the tuple's name followed by a number in square brackets, as in t[1], or by calling a generic function with the tuple and a number as arguments, as in get(t,1). Both guesses are treated as misconceptions for this purpose. The correct form for reading a tuple element is get<index>(tuple), because that form selects the element at compile time.
| Access form | Status for tuple element selection | Reason given |
|---|---|---|
| get<1>(t) | The form used for element access | The index is part of the code, giving compile-time selection |
| t[1] | Named as a misconception for this purpose | Not the way to select a tuple element at compile time |
| get(t,1) | Named as a misconception for this purpose | Not the way to select a tuple element at compile time |
The three access forms as the topic presents them
The source is candid that the get<index> form is not elegant. It looks unusual compared with the bracket syntax many learners already know, and the author says it must be used anyway because it is the way to get compile-time selection of an element. Learners should expect the syntax to feel awkward at first and accept it as the standard form for tuple elements.
Tracing a Program to Find an Unexpected Value
When a tuple access gives a surprising result, the usual cause is a wrong index. When tracing a tuple access, first count the position from zero, then match the value at that position. Work through the program below one line at a time, writing down the index used by each get call and the value it returns.
#include <tuple>
#include <string>
#include <iostream>
int main() {
auto t = std::make_tuple(std::string("apple"), 7, 2.5);
std::cout << std::get<0>(t) << "\n";
std::cout << std::get<1>(t) << "\n";
std::cout << std::get<1>(t) << "\n";
return 0;
}- 1Line 1 creates the tuple, so position 0 holds apple, position 1 holds 7 and position 2 holds 2.5.
- 2The first output line calls get<0>(t), which returns apple.
- 3The second output line calls get<1>(t), which returns 7.
- 4The third output line calls get<1>(t) a second time, which again returns 7. If the programmer meant to print the first element again, the index should have been 0; the unexpected 7 is produced on this line.
- The tuple has three elements: string at 0, int at 1, double at 2
- The third printed line uses index 1 again, not index 0 as the author may have intended
apple
7
7Mistakes Beginners Make with Tuple Indexes
Common mistake Expecting the first element at index 1
What it looks likeReading get<1>(t) and assuming it returns the string in a tuple that begins with a string.
Why it failsTuple elements are numbered starting from zero, so the first element is at index 0.
Do this insteadRead get<0>(t) for the first element and get<1>(t) for the second.
Common mistake Using t[1] to read a tuple element
What it looks likeWriting t[1] to get the second value of a tuple and expecting it to work the way it does with an ordinary array.
Why it failsThe source names t[1] as a misconception for tuple element selection.
Do this insteadUse get<1>(t) to select the element at position 1.
Common mistake Treating get(t,1) as the standard tuple access
What it looks likeWriting get(t,1) because it looks like a generic function call with the tuple and a number.
Why it failsThe source names
get(t,1)as a misconception for compile-time element selection.Do this insteadWrite get<1>(t), placing the index inside the angle brackets.
Common mistake Looking for names on a tuple's elements
What it looks likeTrying to reach the elements of a tuple by names such as first and second, as is done with a pair.
Why it failsA tuple has no names for its elements, so position is the only handle available.
Do this insteadIdentify each element by its zero-based index.
Practicing Tuple Index Reads
A tuple t is created with make_tuple from the string "red", the int 3 and the double 1.5, in that order. Predict the output of the statement std::cout << std::get<2>(t) << and then check your answer by tracing the positions from zero.
Hints
- Count the positions from zero: the string is at 0, the int at 1 and the double at 2.
- Index 2 selects the third element, which is the double.
Key Takeaways
- Tuple elements are numbered starting from zero, so the first element is at index 0 and the second at index 1.
- A tuple element is read with get<index>(tuple), where the index sits inside the angle brackets.
- The get<index> form is used because it gives compile-time selection;
get(t,1)and t[1] are named as misconceptions for this purpose. - When an access returns an unexpected value, trace the program line by line, count each index from zero, and match the value at that position.