C++ Module 5 – Arrays, vectors and aggregate types
std::pair, std::tuple and structured bindings
Group values with std::pair and std::tuple, unpack them with structured bindings, avoid hidden copies in map loops and sort by several keys with std::tie.
What you will learn
- Group related values with std::pair and std::tuple
- Write structured bindings that unpack pairs, tuples, arrays and structs
- Sort records by several keys with std::tie and tuple comparison
- Choose between a pair, a tuple and a named struct for a result
Before you start
What you’ll learn · Before you start
What you will learn
- Group related values with std::pair and std::tuple
- Write structured bindings that unpack pairs, tuples, arrays and structs
- Sort records by several keys with std::tie and tuple comparison
- Choose between a pair, a tuple and a named struct for a result
Before you start
On this page
std::pair holds two values, called first and second; std::tuple holds any fixed number of values, each of its
own type. Both group values without declaring a struct, which suits short-lived results: a function that returns a
name and a score, or an element of a std::map, which is a pair of a key and its value. Structured bindings,
added in C++17, unpack such a group into names in one declaration, auto [name, marks] = record;, and they work for
arrays and for structs with public members too. When the values mean something beyond the next line or two, a struct
with named members is still the clearer choice.
This lesson builds pairs and tuples, shows what a structured binding really declares, measures the copies that the
wrong loop over a map makes, sorts records by several keys with std::tie, and ends with two C++26 additions. Every
example runs with Apple clang 21 and GCC 16.
std::pair holds two values
std::pair, from <utility>, is a struct with two public members, first and second, and their types are its
template arguments:
// std::pair holds two values, called first and second.
#include <algorithm>
#include <map>
#include <print>
#include <string>
#include <type_traits>
#include <utility>
int main() {
std::pair<std::string, int> top{"Asha", 91};
std::println("{} scored {}", top.first, top.second);
std::pair guessed{"Asha", 91}; // the compiler deduces the types from the values
std::println("pair<const char*, int>: {}", std::is_same_v<decltype(guessed), std::pair<const char*, int>>);
using namespace std::string_literals;
std::pair fixed{"Asha"s, 91}; // "Asha"s is a std::string
std::println("pair<std::string, int>: {}", std::is_same_v<decltype(fixed), std::pair<std::string, int>>);
std::map<std::string, int> marks{{"Asha", 91}};
auto result = marks.insert({"Asha", 75}); // insert returns an iterator and a bool
std::println("inserted: {}, value kept: {}", result.second, result.first->second);
auto range = std::minmax({72, 91, 85, 64}); // so does std::minmax: the smallest and the largest
std::println("lowest {}, highest {}", range.first, range.second);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra pair_basics.cpp -o pair_basics && ./pair_basics
g++-16 -std=c++23 -Wall -Wextra pair_basics.cpp -o pair_basics-gcc && ./pair_basics-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra pair_basics.cpp -o pair_basics && ./pair_basics Asha scored 91 pair<const char*, int>: true pair<std::string, int>: true inserted: false, value kept: 91 lowest 64, highest 91 $ g++-16 -std=c++23 -Wall -Wextra pair_basics.cpp -o pair_basics-gcc && ./pair_basics-gcc Asha scored 91 pair<const char*, int>: true pair<std::string, int>: true inserted: false, value kept: 91 lowest 64, highest 91
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pair_basics_session.py
Three things are worth noticing in the output:
- Without template arguments, the compiler deduces the types from the values, and a string literal such as
"Asha"is aconst char*, not astd::string. Comparing two such pairs compares addresses, not text. Write the types out, or use thessuffix fromstd::string_literals, which makes"Asha"sastd::string. - The standard library returns pairs where it has two results.
std::map::insertreturns the position of the element and whether it was inserted: herefalse, because the key"Asha"was already there, so the old value 91 stays. std::minmaxreturns the smallest and the largest value as a pair, found in one pass.
Pairs compare like words in a dictionary: first decides, and second only when the firsts are equal.
std::tuple holds any number of values
std::tuple, from <tuple>, generalises the pair to any number of elements. There are no member names, so you reach
an element by its position with std::get<0>(t), or by its type, std::get<double>(t), when that type occurs only
once in the tuple:
// std::tuple holds a fixed number of values, each with its own type.
#include <print>
#include <string>
#include <tuple>
std::tuple<std::string, int, double> topper() {
return {"Asha", 12, 91.5};
}
int main() {
auto t = topper();
std::println("{} (roll {}): {}", std::get<0>(t), std::get<1>(t), std::get<2>(t));
std::println("marks, found by type: {}", std::get<double>(t));
std::println("elements: {}", std::tuple_size_v<decltype(t)>);
std::string name;
int roll = 0;
std::tie(name, roll, std::ignore) = topper(); // assigns to variables that already exist
std::println("{} has roll number {}", name, roll);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra tuple_basics.cpp -o tuple_basics && ./tuple_basics
g++-16 -std=c++23 -Wall -Wextra tuple_basics.cpp -o tuple_basics-gcc && ./tuple_basics-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra tuple_basics.cpp -o tuple_basics && ./tuple_basics Asha (roll 12): 91.5 marks, found by type: 91.5 elements: 3 Asha has roll number 12 $ g++-16 -std=c++23 -Wall -Wextra tuple_basics.cpp -o tuple_basics-gcc && ./tuple_basics-gcc Asha (roll 12): 91.5 marks, found by type: 91.5 elements: 3 Asha has roll number 12
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 tuple_basics_session.py
std::tuple_size_v gives the number of elements at compile time. std::tie(name, roll, std::ignore) makes a tuple of
references to existing variables, so assigning a tuple to it copies each element into its variable, and
std::ignore throws one element away. This was the usual way to unpack a returned tuple before C++17.
Positions such as std::get<2>(t) say nothing about what the value is, and a reader has to look up the function to
learn which element is which. That is the problem structured bindings solve.
Structured bindings unpack in one declaration
A structured binding declares a name for every element of an object at once:
// Structured bindings name each element of a struct, an array or a tuple in one declaration.
#include <print>
#include <string>
#include <tuple>
struct Student {
std::string name;
int marks = 0;
};
int main() {
Student s{"Meera", 85};
auto [name, marks] = s; // names for the members of a copy of s
marks = 50; // changes the copy only
std::println("{}: copy {}, original {}", name, marks, s.marks);
auto& [same_name, same_marks] = s; // names for the members of s itself
same_marks = 95;
std::println("{}: original now {}", same_name, s.marks);
int sides[3]{3, 4, 5};
auto [a, b, c] = sides; // an array
std::println("{} + {} > {}: {}", a, b, c, a + b > c);
auto [who, roll, score] = std::tuple{std::string{"Dev"}, 7, 72.5}; // a tuple
std::println("{} (roll {}): {}", who, roll, score);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra bindings.cpp -o bindings && ./bindings
g++-16 -std=c++23 -Wall -Wextra bindings.cpp -o bindings-gcc && ./bindings-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra bindings.cpp -o bindings && ./bindings Meera: copy 50, original 85 Meera: original now 95 3 + 4 > 5: true Dev (roll 7): 72.5 $ g++-16 -std=c++23 -Wall -Wextra bindings.cpp -o bindings-gcc && ./bindings-gcc Meera: copy 50, original 85 Meera: original now 95 3 + 4 > 5: true Dev (roll 7): 72.5
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bindings_session.py
auto [name, marks] = s; does not make two independent variables. It declares one unnamed variable initialised
from s, here a copy of it, and makes name and marks names for the members of that unnamed variable. That is why
assigning 50 to marks changed the copy and left s.marks at 85. With auto&, the unnamed variable is a reference
to s, so same_marks = 95 changed s itself, and const auto& gives read-only names without a copy.
What a structured binding declares, with auto and with auto&
Text description of the diagram
The diagram compares two declarations that unpack the same Student s, whose name is "Meera" and whose marks are 85.
- auto [name, marks] = s; creates an unnamed variable that is a copy of s. The names name and marks refer to the members of that copy, so changing marks leaves s unchanged.
- auto& [name, marks] = s; creates an unnamed reference to s. The names name and marks then refer to the members of s itself, so assigning to marks changes s.
In both cases name and marks are not separate variables of their own: they are names for the members of the unnamed variable.
A structured binding can unpack three kinds of object:
- an array, such as
int sides[3], whose size the compiler knows; - a tuple-like type:
std::pair,std::tuple,std::array, and any type that describes its elements throughstd::tuple_size,std::tuple_elementandget; - a class whose non-static data members are all public, such as the aggregates of the previous lesson, with one name per member in declaration order (inside the class’s own member functions, private members count too).
The number of names must equal the number of elements exactly. A std::vector cannot be unpacked, because its size
is known only at run time, and outside its own members a class with a private data member cannot either.
Loops over a map without copies
A std::map<std::string, Marks> stores its elements as std::pair<const std::string, Marks>: the key is const, so
that nobody can change it and break the map’s order. A range-based for loop with structured bindings is the
natural way to read them, but the declaration decides whether every element is copied. Marks below counts its own
copies:
// How many copies does each loop over a map make? Marks counts every copy of itself.
#include <map>
#include <print>
#include <string>
#include <utility>
struct Marks {
int value = 0;
static inline int copies = 0;
Marks(int v) : value{v} {}
Marks(const Marks& other) : value{other.value} { ++copies; }
};
int main() {
std::map<std::string, Marks> marks{{"Asha", 91}, {"Dev", 72}, {"Meera", 85}};
int total = 0;
Marks::copies = 0;
for (auto [name, m] : marks) total += m.value;
std::println("auto [name, m] {} copies", Marks::copies);
Marks::copies = 0;
for (const auto& [name, m] : marks) total += m.value;
std::println("const auto& [name, m] {} copies", Marks::copies);
Marks::copies = 0;
for (const std::pair<std::string, Marks>& p : marks) total += p.second.value;
std::println("const pair<string, Marks>& {} copies", Marks::copies);
Marks::copies = 0;
for (const std::pair<const std::string, Marks>& p : marks) total += p.second.value;
std::println("const pair<const string, Marks>& {} copies", Marks::copies);
for (auto& [name, m] : marks) m.value += 5; // the values can change; the keys are const
std::println("Asha now has {} (the four loops added up {})", marks.at("Asha").value, total);
} from session import run
# -Wall makes both compilers point at the loop that copies without saying so.
run("""
clang++ -std=c++23 -Wall -Wextra map_copies.cpp -o map_copies && ./map_copies
g++-16 -std=c++23 -Wall -Wextra map_copies.cpp -o map_copies-gcc && ./map_copies-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra map_copies.cpp -o map_copies && ./map_copies
map_copies.cpp:27:47: warning: loop variable 'p' of type 'const std::pair<std::string, Marks> &' (aka 'const pair<basic_string<char>, Marks> &') binds to a temporary constructed from type 'reference' (aka 'pair<const std::string, Marks> &') [-Wrange-loop-construct]
27 | for (const std::pair<std::string, Marks>& p : marks) total += p.second.value;
| ^
map_copies.cpp:27:10: note: use non-reference type 'std::pair<std::string, Marks>' (aka 'pair<basic_string<char>, Marks>') to make construction explicit or type 'const value_type &' (aka 'const pair<const std::string, Marks> &') to prevent copying
27 | for (const std::pair<std::string, Marks>& p : marks) total += p.second.value;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
auto [name, m] 3 copies
const auto& [name, m] 0 copies
const pair<string, Marks>& 3 copies
const pair<const string, Marks>& 0 copies
Asha now has 96 (the four loops added up 992)
$ g++-16 -std=c++23 -Wall -Wextra map_copies.cpp -o map_copies-gcc && ./map_copies-gcc
map_copies.cpp: In function 'int main()':
map_copies.cpp:27:47: warning: loop variable 'p' of type 'const std::pair<std::__cxx11::basic_string<char>, Marks>&' binds to a temporary constructed from type 'std::pair<const std::__cxx11::basic_string<char>, Marks>' [-Wrange-loop-construct]
27 | for (const std::pair<std::string, Marks>& p : marks) total += p.second.value;
| ^
map_copies.cpp:27:47: note: use non-reference type 'const std::pair<std::__cxx11::basic_string<char>, Marks>' to make the copy explicit or 'const std::pair<const std::__cxx11::basic_string<char>, Marks>&' to prevent copying
auto [name, m] 3 copies
const auto& [name, m] 0 copies
const pair<string, Marks>& 3 copies
const pair<const string, Marks>& 0 copies
Asha now has 96 (the four loops added up 992)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 map_copies_session.py
The counts are the same with both compilers:
auto [name, m]copies every element, key and value, into the unnamed variable: 3 copies.const auto& [name, m]binds to each element where it is: no copy.const std::pair<std::string, Marks>&looks like a reference, yet copies every element. The element type is a pair with aconst std::string, a different type, so the compiler builds a temporary pair of the written type for each element and binds the reference to that. Both compilers point at this loop with-Wall(-Wrange-loop-construct).- Naming the right type,
const std::pair<const std::string, Marks>&, avoids the copy, butconst auto&is shorter and cannot get the type wrong.
To change the values in place, write auto&: for (auto& [name, m] : marks) m.value += 5; changes the map, while an
attempt to assign to name does not compile, because the key is const. With a few strings the copies cost little,
but in a loop over a large map of large values they cost real time, and nothing in the code says that they happen.
Sorting by several keys with std::tie
Tuples compare lexicographically: the first elements decide, and only when they are equal do the second elements,
and so on. std::tie(a.marks, a.name) builds a tuple of references to the members, without copying them, so
comparing two such tuples compares the records by marks and then by name. Swapping a and b in one position turns
that key around:
// Highest marks first, and names from A to Z among equal marks, by comparing std::tie tuples.
#include <algorithm>
#include <print>
#include <string>
#include <tuple>
#include <vector>
struct Student {
std::string name;
int marks = 0;
};
int main() {
std::println("(2, 9) < (3, 0): {}", std::tuple{2, 9} < std::tuple{3, 0}); // first elements decide
std::println("(3, 1) < (3, 0): {}", std::tuple{3, 1} < std::tuple{3, 0}); // a tie: the second decides
std::vector<Student> students{{"Kabir", 72}, {"Asha", 91}, {"Dev", 72}, {"Meera", 85}, {"Arjun", 91}};
std::ranges::sort(students, [](const Student& a, const Student& b) {
// b's marks against a's: a larger mark sorts first. Then a's name against b's: A to Z.
return std::tie(b.marks, a.name) < std::tie(a.marks, b.name);
});
for (const auto& [name, marks] : students) std::println("{:>3} {}", marks, name);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra rank_students.cpp -o rank_students && ./rank_students
g++-16 -std=c++23 -Wall -Wextra rank_students.cpp -o rank_students-gcc && ./rank_students-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra rank_students.cpp -o rank_students && ./rank_students (2, 9) < (3, 0): true (3, 1) < (3, 0): false 91 Arjun 91 Asha 85 Meera 72 Dev 72 Kabir $ g++-16 -std=c++23 -Wall -Wextra rank_students.cpp -o rank_students-gcc && ./rank_students-gcc (2, 9) < (3, 0): true (3, 1) < (3, 0): false 91 Arjun 91 Asha 85 Meera 72 Dev 72 Kabir
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 rank_students_session.py
The comparison std::tie(b.marks, a.name) < std::tie(a.marks, b.name) puts b.marks on the left, so a student with
more marks sorts first, and keeps a.name on the left, so equal marks fall back to alphabetical order: Arjun before
Asha, Dev before Kabir. The previous lesson’s defaulted operator<=> always compares in declaration order, ascending;
std::tie lets each sort choose its own keys and directions without changing the struct. It needs named variables
or members, though: for a key that is computed, such as a total, build a std::tuple of values instead.
pair, tuple or a named struct?
All three group values, and all three work with structured bindings, so the choice is about the reader:
| Criterion | std::pair | std::tuple | A struct |
|---|---|---|---|
| Names | first, second | Positions: get<0> … | Your own |
| Parts | Exactly 2 | Any fixed number | Any |
| Compare | Built in | Built in | With = default |
| Unpack | Yes | Yes | If members are public |
| When to choose | A key with its value; library results | Generic code; std::tie keys | Values with a meaning |
The C++ Core Guidelines (rule F.21) prefer a struct for a function that returns several values: Summary with
members min, max and mean tells the caller what each value is, and a structured binding unpacks it just as
easily as a tuple. Keep pairs and tuples for code that does not know what the values mean, such as templates that
handle any number of types, and for keys and results whose meaning is obvious on the spot.
C++26: the placeholder _ and binding packs
C++26, whose draft is complete but not yet published as an ISO standard, adds two features to structured bindings.
Both compilers accept them with -std=c++2c, the option for the C++26 draft:
- A placeholder,
_(paper P2169R4), names an element you do not need. Unlike an ordinary name, it may appear several times in one declaration:auto [_, physics, _] = scores;. GCC has supported it since version 14 and Clang since version 18. - Binding packs (paper P1061R10) give a group of names to “all the remaining elements”:
auto [first, ...rest] = value;. A pack works only inside a template, and you use it like any other pack, withsizeof...or a fold expression. GCC 16 and Clang 21 are the first versions with it.
// C++26 previews: the placeholder _ and structured binding packs (build with -std=c++2c).
#include <print>
#include <tuple>
struct Scores {
int maths = 0;
int physics = 0;
int chemistry = 0;
};
// A pack, ...parts, gets one name for every element, whatever the type has. Packs work only in templates.
template <class T>
int total(const T& value) {
const auto& [...parts] = value;
return (0 + ... + parts);
}
template <class T>
int count_after_first(const T& value) {
const auto& [first, ...rest] = value;
(void)first;
return sizeof...(rest);
}
int main() {
Scores s{91, 78, 85};
auto [_, physics, _] = s; // _ can be repeated: these elements are not needed
std::println("physics {}", physics);
std::println("total of a struct {}, of a tuple {}", total(s), total(std::tuple{1, 2, 3, 4}));
std::println("elements after the first: {}", count_after_first(s));
} from session import run
# -std=c++2c is the switch for the C++26 draft in both compilers.
run("""
clang++ -std=c++2c -Wall -Wextra cpp26_bindings.cpp -o cpp26_bindings && ./cpp26_bindings
g++-16 -std=c++2c -Wall -Wextra cpp26_bindings.cpp -o cpp26_bindings-gcc && ./cpp26_bindings-gcc
""") Output
$ clang++ -std=c++2c -Wall -Wextra cpp26_bindings.cpp -o cpp26_bindings && ./cpp26_bindings physics 78 total of a struct 254, of a tuple 10 elements after the first: 2 $ g++-16 -std=c++2c -Wall -Wextra cpp26_bindings.cpp -o cpp26_bindings-gcc && ./cpp26_bindings-gcc physics 78 total of a struct 254, of a tuple 10 elements after the first: 2
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cpp26_bindings_session.py
total adds up the members of any struct, tuple or array without knowing how many there are. Before C++26 that took
std::apply and a lambda for tuples, and for structs a library or a separate version for each number of members. An earlier revision of the paper also
allowed packs outside templates; the final one does not, and both compilers reject auto [first, ...rest] in an
ordinary function.
Your turn: one summary, two shapes
summary.h declares two functions that compute the smallest value, the largest and the mean of a list of integers.
summarise returns the struct Summary; summarise_tuple returns the same results as a
std::tuple<int, int, double>. Write both. You may assume the list is never empty.
C header · summary.h
// Starter code for "Your turn": write summarise and summarise_tuple.
#pragma once
#include <tuple>
#include <vector>
struct Summary {
int min = 0;
int max = 0;
double mean = 0.0;
bool operator==(const Summary&) const = default;
};
// The smallest value, the largest and their mean. values is never empty.
inline Summary summarise(const std::vector<int>& values) {
// TODO
(void)values;
return {};
}
// The same three results as a tuple, in the same order.
inline std::tuple<int, int, double> summarise_tuple(const std::vector<int>& values) {
// TODO: a structured binding on summarise(values) makes this short
(void)values;
return {};
} C++ · summary_tests.cpp
// Sample tests for summary.h. Build: clang++ -std=c++23 -Wall -Wextra summary_tests.cpp -o summary_tests
#include <print>
#include <tuple>
#include <vector>
#include "summary.h"
int checks = 0;
int failures = 0;
void check(bool ok, const char* what) {
++checks;
if (!ok) {
++failures;
std::println("FAIL {}", what);
}
}
int main() {
std::vector<int> marks{72, 91, 64, 85};
check(summarise(marks) == Summary{.min = 64, .max = 91, .mean = 78.0}, "min, max and mean of 72, 91, 64, 85");
check(summarise({-5}) == Summary{.min = -5, .max = -5, .mean = -5.0}, "one value is the min, the max and the mean");
check(summarise({1, 2}).mean == 1.5, "the mean keeps its fraction");
auto [low, high, mean] = summarise_tuple(marks);
check(low == 64 && high == 91 && mean == 78.0, "the tuple holds min, max and mean, in that order");
check(summarise_tuple({7}) == std::tuple{7, 7, 7.0}, "the tuple of one value");
std::println("{} of {} checks passed", checks - failures, checks);
return failures == 0 ? 0 : 1;
} from session import run
# The sample tests run against the starter code, before any TODO is done.
run("""
clang++ -std=c++23 -Wall -Wextra summary_tests.cpp -o summary_tests && ./summary_tests
g++-16 -std=c++23 -Wall -Wextra summary_tests.cpp -o summary_tests-gcc && ./summary_tests-gcc
""") Output
$ clang++ -std=c++23 -Wall -Wextra summary_tests.cpp -o summary_tests && ./summary_tests FAIL min, max and mean of 72, 91, 64, 85 FAIL one value is the min, the max and the mean FAIL the mean keeps its fraction FAIL the tuple holds min, max and mean, in that order FAIL the tuple of one value 0 of 5 checks passed [exit status 1] $ g++-16 -std=c++23 -Wall -Wextra summary_tests.cpp -o summary_tests-gcc && ./summary_tests-gcc FAIL min, max and mean of 72, 91, 64, 85 FAIL one value is the min, the max and the mean FAIL the mean keeps its fraction FAIL the tuple holds min, max and mean, in that order FAIL the tuple of one value 0 of 5 checks passed [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 summary_session.py
When both functions are right, the tests print 5 of 5 checks passed and the program exits with status 0. Two hints:
std::ranges::minmax(values) returns the smallest and the largest value in one pass, as a struct you can unpack with
a structured binding, and summarise_tuple can unpack summarise(values) the same way and return a tuple of the
three names. Then compare the call sites in the tests: summarise(marks).mean names what it reads, while the tuple
needs the reader to remember which position holds the mean.
Interview questions
Fresher · What does auto [a, b] = p; declare, and how is it different from auto& [a, b] = p;?
It declares one unnamed variable and two names. auto [a, b] = p; copies p into the unnamed variable, and a and
b refer to the elements of that copy, so changing them leaves p as it was. With auto&, the unnamed variable is a
reference to p, so a and b refer to the elements of p itself and assigning to them changes p. const auto&
gives read-only access without a copy, which is the usual choice in loops such as
for (const auto& [key, value] : m). The number of names must equal the number of elements, and the object must be
an array, a tuple-like type such as std::pair or std::tuple, or a class whose non-static data members are all
public. A good answer also mentions that a plain auto copies every element of a container it loops over.
Key takeaways
std::pairhasfirstandsecond;std::tuplehas any fixed number of elements, read withstd::get<i>. A string literal in a deduced pair is aconst char*, not astd::string.auto [x, y] = e;declares an unnamed copy ofeand names for its elements;auto&andconst auto&bind toeitself.- Structured bindings unpack arrays, tuple-like types and classes with only public data members, with exactly one name per element.
- Loop over a map with
const auto& [key, value]. A plainauto, or a reference to the wrong pair type, copies every element, and-Wallwarns about the second. - Tuples compare element by element, so
std::tie(b.marks, a.name) < std::tie(a.marks, b.name)sorts by marks from high to low and then by name. - Prefer a named struct for results that mean something; C++26 adds
_for unwanted elements and binding packs in templates.
Check yourself
6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
References
- Working Draft, Programming Languages: C++ (N4950) (ISO/IEC JTC1/SC22/WG21)
- The Standard (current ISO C++ standard and working drafts) (Standard C++ Foundation)
- N5051 Editors' Report: Programming Languages, C++ (ISO/IEC JTC1/SC22/WG21)
- P0217R3: Wording for structured bindings (ISO/IEC JTC1/SC22/WG21)
- P1061R10: Structured Bindings can introduce a Pack (ISO/IEC JTC1/SC22/WG21)
- P2169R4: A nice placeholder with no name (ISO/IEC JTC1/SC22/WG21)
- C++ Core Guidelines: F.21, to return multiple out values, prefer returning a struct (Standard C++ Foundation)
- C++ Standards Support in GCC (GNU Project)
- C++ Support in Clang (LLVM Project)
- Diagnostic flags in Clang (LLVM Project)
- Options to Request or Suppress Warnings (Using the GNU Compiler Collection) (GNU Project)
Report a problem with this lesson
Kept only in this browser. Your Learn progress