Skip to content

C++ structured bindings

Structured bindings give names to the individual parts of an array, tuple-like object, or simple structure. They were introduced in C++17.

Simple example

#include <iostream>
#include <utility>

int main()
{
    const std::pair<int, int> position{4, 7};
    const auto [x, y] = position;

    std::cout << "x=" << x << ", y=" << y << '\n';
}

auto [x, y] creates two names from the two elements of position. Compile it as C++17 or newer:

g++ -std=c++17 -Wall -Wextra -pedantic example.cpp -o example
./example
x=4, y=7

Supported types

Structures

1
2
3
4
5
6
7
8
struct SensorReading
{
    int id;
    double temperature;
};

const SensorReading reading{3, 24.5};
const auto [id, temperature] = reading;

The names follow member declaration order, not their names. The number of bindings must match the number of decomposed members.

Arrays

int coordinates[]{10, 20, 30};
auto [x, y, z] = coordinates;

Pairs and tuples

1
2
3
4
#include <tuple>

const auto record = std::make_tuple(7, "camera", true);
const auto [id, name, enabled] = record;

std::pair, std::tuple, and other tuple-like types can be decomposed when their element count and access operations are defined.

Copies, references, and const

Choose the qualifier according to whether you need a copy or access to the original object:

Binding Behavior
auto [x, y] = value; Work with a copy.
auto& [x, y] = value; Modify the original elements.
const auto& [x, y] = value; Read the original elements without copying.
auto&& [x, y] = expression; Preserve whether the expression is an lvalue or rvalue.
1
2
3
4
5
6
std::pair<int, int> position{4, 7};

auto& [x, y] = position;
x = 10;

std::cout << position.first; // 10

Use const auto& in read-only loops

It avoids copying each element while preventing accidental modification.

Structured bindings in loops

They make key-value iteration easier to read:

#include <iostream>
#include <map>
#include <string>

const std::map<std::string, int> scores{
    {"Ada", 10},
    {"Bjarne", 12}
};

for (const auto& [name, score] : scores)
    std::cout << name << ": " << score << '\n';

For a map element, the first binding is the key and the second is the mapped value.

Returning multiple values

A function can return a small structure or tuple and let the caller name each result:

struct DivisionResult
{
    int quotient;
    int remainder;
};

DivisionResult divide(int value, int divisor)
{
    return {value / divisor, value % divisor};
}

const auto [quotient, remainder] = divide(17, 5);

Prefer a named structure when the fields have domain meaning. A pair or tuple is suitable for a small, obvious local result.

Use in an if statement

A structured binding can appear in an if initializer. This is common with container insertion:

1
2
3
4
5
6
std::map<std::string, int> scores;

if (const auto [iterator, inserted] = scores.insert({"Ada", 10}); inserted)
    std::cout << "Added " << iterator->first << '\n';
else
    std::cout << "The key already exists\n";

Both names exist only inside the if and else statements.

Common mistakes

  • Using the wrong number of names causes a compile error.
  • Omitting & creates a copy, so changes do not affect the original object.
  • Binding names are chosen by position; renaming structure members does not rename the bindings.
  • Large values can be expensive to copy. Prefer const auto& when only reading them.
  • _ is an ordinary variable name in C++; it does not discard an unwanted element.

Quiz

1. Does this modify the pair?

1
2
3
4
std::pair<int, int> point{2, 3};
auto [x, y] = point;
x = 9;
std::cout << point.first;
Choose one answer:

2. Which binding is best for a read-only map loop?

Choose one answer:

3. What happens when the binding count is wrong?

std::tuple<int, int, int> values{1, 2, 3};
auto [first, second] = values;
Choose one answer:

Further reading