Structured Bindings (C++17)

Decompose objects into individual named variables

The Problem Before C++17

Accessing individual members of pairs, tuples, or structs required either using first/second or std::tie.

// C++11/14: Verbose tuple/pair access
std::map<std::string, int> scores;
auto result = scores.insert({"Alice", 100});

// Using iterator access
auto iterator = result.first;
bool success = result.second;

// Or using std::tie (creates temporary objects)
std::map<std::string, int>::iterator it;
bool inserted;
std::tie(it, inserted) = scores.insert({"Bob", 95});

// Still awkward: what do 'first' and 'second' mean?

C++17: Structured Bindings

Structured bindings allow unpacking aggregates directly into named variables.

// C++17: Clean, readable unpacking
std::map<std::string, int> scores;
auto [iterator, success] = scores.insert({"Alice", 100});

// 'iterator' and 'success' are now individual variables
if (success) {
    std::cout << "Inserted: " << iterator->first 
              << " = " << iterator->second << std::endl;
}

// Much clearer than result.first / result.second!

Supported Types

std::pair

std::pair<int, double> p{1, 3.14};
auto [i, d] = p;
// i = 1, d = 3.14

std::tuple

std::tuple<int, char, double> t{1, 'a', 3.14};
auto [i, c, d] = t;
// i = 1, c = 'a', d = 3.14

Arrays

int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;
// a = 1, b = 2, c = 3

Structs (Public members)

struct Point { int x; int y; };
Point p{10, 20};
auto [px, py] = p;
// px = 10, py = 20

Reference Bindings

Use & or && modifiers to bind by reference:

std::pair<int, int> p{1, 2};

// By value (copies)
auto [a, b] = p;
a = 10;  // p.first unchanged

// By reference (aliases)
auto& [ref_a, ref_b] = p;
ref_a = 10;  // p.first is now 10!

// Const reference (read-only view)
const auto& [ca, cb] = p;
// ca = 10;  // ERROR: read-only

// Rvalue reference (for move semantics)
auto&& [rref_a, rref_b] = std::move(p);

Practical Applications

1. Range-based For with Maps

std::map<std::string, int> scores = {
    {"Alice", 100},
    {"Bob", 95},
    {"Charlie", 87}
};

// C++17: Clean iteration over maps
for (const auto& [name, score] : scores) {
    std::cout << name << ": " << score << std::endl;
}

// No more iterator->first / iterator->second!

2. Multiple Return Values

// Return multiple values elegantly
std::tuple<int, int, int> divide(int dividend, int divisor) {
    return {
        dividend / divisor,      // quotient
        dividend % divisor,      // remainder
        dividend % divisor == 0  // is_exact
    };
}

// Unpack result
auto [quotient, remainder, is_exact] = divide(17, 5);
std::cout << "17 / 5 = " << quotient 
          << " remainder " << remainder << std::endl;

3. Decomposing Structs

struct Config {
    std::string host;
    int port;
    bool use_ssl;
};

Config load_config() {
    return {"localhost", 8080, false};
}

// Direct unpacking
auto [host, port, ssl] = load_config();
std::cout << "Connecting to " << host << ":" << port << std::endl;

Structured Bindings with Attributes

Use [[maybe_unused]] when you don't need all members:

// Only care about the iterator, not the success flag
auto [it, [[maybe_unused]] success] = map.insert({"key", value});

// Or use anonymous binding (C++26 proposal, some compilers support)
// auto [it, _] = map.insert(...);  // Not standard yet

Implementation Details

Structured bindings create hidden variables ("e") that hold the actual object, with the named bindings becoming aliases:

// What the compiler generates:
auto [x, y] = point;

// Roughly equivalent to:
auto __e = point;  // Hidden variable
auto& x = __e.x;   // Alias to member
auto& y = __e.y;   // Alias to member

// This is why reference modifiers apply to the hidden variable
auto& [rx, ry] = point;
// auto& __e = point;  // Reference to original
// auto& rx = __e.x;
// auto& ry = __e.y;

Best Practices

  • Use structured bindings to improve code readability
  • Prefer const auto& for read-only access
  • Use meaningful names that describe the data, not the type
  • Avoid structured bindings with large objects unless using references
  • Don't use for private members (not supported)