Using the pair Utility
Make_pair() is a guideline to consider, not a requirement, and explicit types remain valid.
Choosing a Container That Fits the Data
A container is a structure that holds other values. Writing C++ well means choosing a container that matches the shape of your data, not simply the first one that comes to mind. This article looks at two practical guidelines from the C++ Standard Library. The first says that when the size of a sequence is fixed at compile time, an array is a sensible choice. The second says that when you create a pair, make_pair() can simplify the code by letting the compiler deduce the types.
Both guidelines are about what the program can know ahead of time. The size of the container and the types inside a pair are facts that the compiler can sometimes settle before the program runs. Following the trace in this article, you will see how each choice changes what the program knows at each step.
What a Constexpr Size Means
A constexpr value is a value known when the program is compiled, not when it runs. Use an array when a sequence has a constexpr size, meaning its length is fixed at compile time.
A good example is the number of sensor channels a program is built to read. If that number is decided ahead of time and does not change while the program runs, it can be declared as a constexpr value and used as the size of the array. A constexpr size is what makes an array a good fit. When the size of a sequence is decided only at run time, a different choice is needed.
- kChannels is fixed when the program is compiled, so it can serve as the array size.
- The array holds exactly four elements, and that count never changes afterward.
- readings[2] reaches the third element directly by its index.
The example above applies the size guideline. Read the declaration first, then the access that follows it. The number of readings comes from a constexpr value, and the array is declared with that value as its size.
Tracing What the Program Knows
Follow the array example one stage at a time. Each stage adds to or relies on what the program already knows about the container. The diagram below shows the sequence of facts.
The first fact is settled at compile time: the size is 4. When the declaration runs, the array has room for four elements, and that number never changes afterward, because it came from a compile-time value. Each element is then reached by its index. The program does not need to ask how many elements exist before reading one, because the size was settled earlier. Keeping this sequence of facts in mind shows why a fixed size is a useful design choice.
Indices Into a Fixed Block of Elements
Each element of the array sits at a position that its index identifies. Index 0 is the first element, index 1 is the second, and so on up to the last one. Because the size is fixed, the set of valid indices is also fixed. The diagram below maps each index of the readings array to the value stored there.
Choosing Between an Array and a Resizable Container
The array guideline depends on the size being known at compile time. If the number of elements is decided only while the program runs, a fixed-size array is the wrong tool, and a different, resizable container is the better choice. The table below compares the two on the points that matter when choosing.
| Question | Array with a constexpr size | Resizable container |
|---|---|---|
| When is the size settled? | At compile time, from a constexpr value | While the program runs |
| Can the number of elements change? | No, it stays fixed | Yes, it can grow or shrink |
| What fits the data? | A sequence whose length is a fixed quantity decided ahead of time | A sequence whose length is not known until run time |
| What the program knows at each step | The size is known from the declaration onward | The current size must be checked or tracked as the program runs |
Fixed-size arrays and resizable containers differ in when the size is known and whether it can change.
Creating Pairs With make_pair
A pair holds two values together as one object. The second guideline concerns pairs: when you create one, make_pair() can simplify the code. Make_pair() is a guideline to consider, not a requirement, and explicit types remain valid. Type deduction is the mechanism that makes it work. The compiler works out the element types from the values you give it, so you do not have to write them yourself.
- The first declaration names both element types, int and double, in the type itself.
- The second declaration writes no element types;
make_pair()takes them from the arguments 3 and 2.5. - Both declarations produce a pair that holds an int and a double.
Without make_pair(), you name both types in the declaration. With it, the types come from the arguments. Compare how much you have to type in each case. In the second line, the compiler reads the value 3 and the value 2.5, then chooses the types that match them.
What do you think happens?
Given the values 3 and 2.5, what type is the pair that make_pair() returns?
Reveal answer
std::pair<int, double>
make_pair() deduces each element type from its argument: 3 is an int and 2.5 is a double, so the returned pair holds an int and a double.
Once a pair exists, its two values are reached through its first and second members. The pair does not change the types it was built with, so the values you read from first and second have the types the pair was created with.
Mistakes Beginners Make With Containers and Pairs
Common mistake Using a constexpr size for a count that is only known at run time
What it looks likeconstexpr int n = read_count_from_user(); (a value that comes from input)
Why it failsA constexpr value must be known when the program is compiled. A count read from input is not known then.
Do this insteadUse a resizable container when the number of elements is decided while the program runs.
Common mistake Assuming make_pair() is required
What it looks likeWriting std::make_pair(3, 2.5) when you want to state the types yourself
Why it failsmake_pair()is a guideline to consider, not a requirement. Explicit types remain valid.Do this insteadWrite std::pair<int, double> explicitly whenever naming the types makes the code clearer.
Common mistake Expecting make_pair() to produce a type you did not pass in
What it looks likeauto p = std::make_pair(3, 2.5f); assuming the result holds a double
Why it failsType deduction takes each element type from its argument, and 2.5f is a float.
Do this insteadRead the argument types to predict the pair type, or write the type explicitly.
Common mistake Treating a fixed-size array as if it can grow when more elements arrive
Common mistake Forgetting that the valid indices of a fixed-size array stop at size minus one
Practice Choosing and Predicting
A program reads exactly 6 sensor channels, a fixed hardware count known when the program is built. Would you store the readings in an array with a constexpr size or in a resizable container? Explain which facts the program knows at the declaration.
Hints
- Ask whether the count is known at compile time.
- Think about whether the number of elements can change while the program runs.
Write one declaration that stores the name "alpha" and the value 9 in a pair without naming the element types, then state the type the compiler deduces.
Hints
make_pair()deduces types from its arguments.- A string literal in double quotes has a type that the compiler works out from the literal itself.
Key Takeaways
- Use an array when a sequence has a constexpr size, meaning its length is fixed at compile time.
- A constexpr value is known when the program is compiled, so an array declared with it has a size that never changes afterward.
make_pair()simplifies pair creation through type deduction: the compiler determines the element types from the values, and explicit types remain valid.- Choose the container that matches the shape of your data; when the size is only known at run time, use a different, resizable container.