Structured Bindings: Decomposing Objects
C++17 structured bindings provide a concise syntax for unpacking tuples, pairs, arrays, and structs into individual variables.
Basic Syntax
std::pair<int, std::string> get_person() {
return {42, "Alice"};
}
auto [id, name] = get_person(); // Structured binding
std::cout << id << ": " << name; // 42: Alice
Binding Pairs and Tuples
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << '\\n';
}
std::tuple<int, double, std::string> data{42, 3.14, "hello"};
auto [i, d, s] = data;
Binding Arrays
int arr[] = {1, 2, 3};
auto [a, b, c] = arr; // a=1, b=2, c=3
// Number of bindings must match array size
// auto [x, y] = arr; // Error: 3 elements, 2 bindings
Binding Structs
Works with any struct/class where all members are public:
struct Point {
double x, y;
};
Point p{3.0, 4.0};
auto [x, y] = p; // x=3.0, y=4.0
double distance = std::sqrt(x*x + y*y);
Reference Bindings
std::map<std::string, int> map = {{"key", 42}};
// Reference binding: can modify original
for (auto& [key, value] : map) {
value *= 2; // Modifies map
}
// Const reference: read-only
for (const auto& [key, value] : map) {
std::cout << key << ": " << value << '\\n';
}
Ignoring Values
C++20 allows placeholder with [[maybe_unused]]:
auto [a, [[maybe_unused]] b, c] = std::tuple{1, 2, 3};
// b is not used but no warning
C++26 will support _ as placeholder:
auto [a, _, c] = std::tuple{1, 2, 3}; // C++26
Custom Types
Make custom types decomposable:
class Rectangle {
double width_, height_;
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
// Tuple-like interface
template<size_t I>
double get() const {
if constexpr (I == 0) return width_;
else if constexpr (I == 1) return height_;
}
};
// Specializations for std::tuple_size and std::tuple_element
namespace std {
template<>
struct tuple_size<Rectangle> : integral_constant<size_t, 2> {};
template<size_t I>
struct tuple_element<I, Rectangle> { using type = double; };
}
Rectangle r{3.0, 4.0};
auto [w, h] = r; // w=3.0, h=4.0
Init Statements in if and switch (C++17)
Declare and initialize variables in the condition statement with tighter scope.
if with Init Statement
if (auto it = map.find(key); it != map.end()) {
std::cout << it->second;
// it is in scope here
}
// it is NOT in scope here
Before C++17:
auto it = map.find(key); // it pollutes outer scope
if (it != map.end()) {
std::cout << it->second;
}
switch with Init Statement
switch (auto status = get_status(); status) {
case Status::OK:
// use status
break;
case Status::ERROR:
// use status
break;
}
// status not in scope
Structured Binding in if
Combine init statement with structured binding:
if (auto [iter, inserted] = map.insert({key, value}); inserted) {
std::cout << "Inserted: " << iter->first << '\\n';
} else {
std::cout << "Already exists\\n";
}
Range-Based for Init Statement (C++20)
for (auto v = init_value(); auto& elem : container) {
// v and elem both in scope
process(v, elem);
}
Use Cases
Lock and Check
if (std::lock_guard lock(mutex); condition()) {
// Protected section
}
// Lock released
Optional Chaining
if (auto opt = get_optional(); opt.has_value()) {
std::cout << *opt;
}
Error Handling
if (auto [result, error] = perform_operation(); error) {
handle_error(error);
} else {
use_result(result);
}
Conceptual Check
What's the main benefit of init statements in if/switch?
Runtime Environment
Interactive Lab
1#include <iostream>
2#include <map>
3#include <tuple>
4
5struct Point { double x, y; };
6
7int main() {
8 // Structured bindings with tuple
9 auto [a, b, c] = std::tuple{1, 2.5, "hello"};
10 std::cout << "Tuple: " << a << ", " << b << ", " << c << '\n';
11
12 // Structured bindings with struct
13 Point p{3.0, 4.0};
14 auto [x, y] = p;
15 std::cout << "Point: (" << x << ", " << y << ")\n";
16
17 // Init statement in if
18 std::map<std::string, int> scores{{"Alice", 95}};
19
20 if (auto it = scores.find("Alice"); it != scores.end()) {
21 std::cout << "Alice's score: " << it->second << '\n';
22 }
23
24 // Structured binding in loop
25 for (const auto& [name, score] : scores) {
26 std::cout << name << " scored " << score << '\n';
27 }
28
29 return 0;
30}
System Console
Waiting for signal...