Modern C++ Utility Types

Structured Bindings

If a function returns two or three closely associated values, then the caller has to be willing to accept them as a single object. Structured bindings allow this object to be destructured in a single step into appropriately named variables.

The simplest example unpacks a pair, std::pair.

#include <iostream>
#include <utility>

int main()
{
    // Group these two values into one pair
    std::pair<int, double> p{ 10, 3.14 };
    auto [i, d] = p;
    std::cout << i << ' ' << d << '\n';
}

Here, the pair p contains an integer and a double. The binding auto [i, d] = p declares two variables and initializes them to hold copied values from p.first and p.second, respectively. The following line prints out the contents. You have ordinary variables i and d at this point, which you can use independently of p.

This is just as easy for a std::tuple containing an arbitrary number of elements.

#include <iostream>
#include <tuple>

int main()
{
    // Store several differently typed values in one tuple
    std::tuple<int, double, char> t{ 5, 1.5, 'x' };
    auto [a, b, c] = t;
    std::cout << a << ' ' << b << ' ' << c << '\n';
}

The tuple t contains three values, and the binding auto [a, b, c] = t declares three local variables which are initialized with copies of the values from the tuple, in order. The final line prints them all. This requires, as you'd expect, that the number of names within the brackets must match the number of values in the object they're bound to exactly. Otherwise, it's a compile time error.

Ordinary arrays are unpacked in exactly the same way.

#include <iostream>

int main()
{
    int arr[3]{ 7, 8, 9 };
    // Let the initializer determine this variable type
    auto [x, y, z] = arr;
    std::cout << x << ' ' << y << ' ' << z << '\n';
}

We have an array of three ints, and this statement declares three int variables x, y, and z which get initialized with the array elements. Those elements are then printed. The array itself is left unmodified.

And it's possible for an ordinary C style struct to be unpacked as well.

#include <iostream>

// Define the members that belong to this type
struct Point
{
    int x;
    int y;
};

int main()
{
    Point p{ 4, 2 };
    auto [a, b] = p;
    std::cout << a << ' ' << b << '\n';
}

The Point struct with its two public members is destructured into two local variables named by the elements it's bound to. The printed output is the two values stored in the structure. These are also bound by declaration order.

In all cases, the names used in the bound object are new variables that are initialized to their components and have an independent lifetime; the original object is never modified by the binding operation itself, unless binding to references, which is another topic.

So structured bindings turn an object containing one or two pieces of information into a set of named variables, making your code shorter and cleaner.

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.