Creating Tuples with make_tuple
Tuple elements are numbered starting from zero, unlike pair elements that are named first and second.
A Tuple With Three Slots
A tuple groups several values into a single object. The source builds one from a string, an int, and a double, using make_tuple so that the types are deduced from the values you pass in. Once the tuple exists, each of its values can be read back out, but the way you name a value matters a great deal. Before reading on, try the question below.
What do you think happens?
A tuple t is made with a string, an int, and a double, in that order. What does get<1>(t) return?
Reveal answer
The int, because counting starts at zero and index 1 is the second element
Tuple elements are numbered from zero. The first element is at index 0, so index 1 is the second element, which here is the int. Thinking of index 1 as the first value is the most common early mistake.
Tracing the Positions of a Tuple
Every value in a tuple sits in a numbered position. The diagram below shows the same tuple the source uses: a string, an int, and a double, created with make_tuple. Each position holds one value and has one number. Reading the diagram from left to right gives the order of the elements, and the number under each box is the number you pass to get.
Reading the three positions
A tuple t holds a string, an int, and a double, in that order. Which value does each of get<0>(t), get<1>(t), and get<2>(t) read?
- 1Count the position from zero
The first element is at index 0. Start counting at zero, not one, when you look at the position number.
- 2Match index 0 to the first value
Index 0 is the string, the first element in the tuple's order.
- 3Match index 1 to the second value
Index 1 is the int. Moving one position to the right from the string gives the int.
- 4Match index 2 to the third value
Index 2 is the double, the third and last element.
get<0>(t) reads the string, get<1>(t) reads the int, and get<2>(t) reads the double.
How make_tuple Fills the Slots
make_tuple takes the values you pass it as arguments and places them into the tuple's slots. The order of the arguments determines the order of the slots, and that order is what fixes each element's index. The first argument ends up in the slot numbered zero, the second in the slot numbered one, and so on. The diagram shows that flow for three arguments.
Why Counting Starts at Zero
The elements of a tuple are numbered starting from zero. This differs from a pair, whose elements are named first and second. A tuple has no names for its elements, so position is the only handle you have. 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, which means the first element is never reached with index 1.
A tuple index is the position number of an element inside a tuple. Positions are counted from zero, so the first element has index 0.
| Feature | Pair | Tuple |
|---|---|---|
| How elements are reached | By the names first and second | By position number, starting from zero |
| Names for elements | Yes, first and second | None, so position is the only handle |
| Index of the first element | Not a number; use first | 0 |
Pair elements have names, while tuple elements are reached only by their numbered position.
Retrieving an Element with get
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. The source gives three examples that read the first, second, and third elements of the same tuple. The program below follows that pattern. Trace it line by line, then check the output.
#include <iostream>
#include <string>
#include <tuple>
int main() {
auto t = std::make_tuple(std::string("hello"), 42, 3.5);
std::cout << std::get<0>(t) << "\n";
std::cout << std::get<1>(t) << "\n";
std::cout << std::get<2>(t) << "\n";
return 0;
}- 1Line 6 creates the tuple t. The first argument becomes index 0, the second index 1, and the third index 2.
- 2Line 7 calls get<0>(t). The index 0 selects the first element, so hello is printed.
- 3Line 8 calls get<1>(t). The index 1 selects the second element, so 42 is printed, not hello.
- 4Line 9 calls get<2>(t). The index 2 selects the third element, so 3.5 is printed.
make_tuplebuilds the tuple with a string, an int, and a double, in that order- get<0>(t) reads the element at index 0, the string
- get<1>(t) reads the element at index 1, the int
- get<2>(t) reads the element at index 2, the double
hello
42
3.5Choosing the Access Form
A common first guess is that a tuple's elements can be reached the way any other collection is, by writing the tuple's name followed by a number in square brackets, or by calling a generic function with the tuple and a number as arguments. The source names both of these guesses as misconceptions. Its position is that elements are reached with get<index>(tuple), and that get(t,1) and t[1] are not the way to access tuple elements for this purpose.
| Access form | Status in this material | Reason given |
|---|---|---|
| get<index>(tuple) | The form to use | The index is part of the code itself, so the element is selected at compile time |
| get(t,1) | Named as a misconception | Not the way to select a tuple element at compile time |
| t[1] | Named as a misconception | Not the way to select a tuple element at compile time |
The source's choice of access form for selecting a tuple element.
The index in get<index> is written into the code and is not computed while the program runs. That is what the source means by compile-time selection: the position of the element you want is fixed when the program is compiled. The source is candid that this form is not elegant, calling it ugly, but it is the form it teaches because it is the way to get compile-time selection of an element.
Mistakes When Reading Tuple Positions
Common mistake Expecting get<1> to return the first element
What it looks likeWith t made from hello, 42, and 3.5, reading get<1>(t) and expecting hello
Why it failsPositions are numbered from zero, so index 1 is the second element, which is 42.
Do this insteadCount from zero: index 0 is hello, index 1 is 42, and index 2 is 3.5.
Common mistake Using t[1] because other collections work that way
What it looks likeWriting t[1] to read the second element of a tuple
Why it failsThe source names t[1] as a misconception for accessing tuple elements.
Do this insteadWrite get<1>(t) to read the element at index 1.
Common mistake Using get(t,1) with a number in parentheses
What it looks likeWriting get(t,1) to select the element at index 1
Why it failsThe source names
get(t,1)as a misconception and does not use it for compile-time selection.Do this insteadWrite get<1>(t), with the index inside the angle brackets.
Common mistake Treating a tuple's position numbers as if they started at one, which shifts every value by one place
Tracing a Tuple Program
Read the program below. Before running it, predict what each of the three lines prints, then compare your predictions with the expected output. Identify the line where a reader who counts from one would expect a different value than the program actually prints.
Hints
- Start by writing down the index of each element: 0 for alpha, 1 for 10, and 2 for 2.5.
- Then check which index each get call uses, and match it to the element at that index.
- The unexpected result is the line whose index points to a value you did not expect when counting from one.
10
alpha
2.5- The first printed line uses get<1>, which reads the second element
- The second printed line uses get<0>, which reads the first element
- The third printed line uses get<2>, which reads the third element
Key Takeaways
- Tuple elements are numbered starting from zero, so the first element is at index 0 and is never reached with index 1.
- A tuple has no names for its elements, which is different from a pair whose elements are named first and second; position is the only handle.
make_tupleplaces its arguments into the tuple in order, and that order fixes each element's index.- Elements are retrieved with get<index>(tuple), where the index is part of the code and selected at compile time;
get(t,1)and t[1] are named misconceptions for this purpose. - When tracing a tuple access, first count the position from zero, then match the value at that position to find an unexpected result.