Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are use cases for structured bindings?

C++17 standard introduces a new structured bindings feature, which was initially proposed in 2015 and whose syntactic appearance was widely discussed later.

Some uses for them come to mind as soon as you look through documentation.

Aggregates decomposition

Let's declare a tuple:

std::tuple<int, std::string> t(42, "foo");

Named elementwise copies may be easily obtained with structured bindings in one line:

auto [i, s] = t;

which is equivalent to:

auto i = std::get<0>(t);
auto s = std::get<1>(t);

or

int i;
std::string s;
std::tie(i, s) = t;

References to tuple elements can also be obtained painlessly:

auto& [ir, sr] = t;
const auto& [icr, scr] = t;

So we can do with arrays or structs/classes whose all members are public.

Multiple return values

A convenient way to get multiple return values from a function immediately follows from the above.

What else?

Can you provide some other, possibly less obvious use cases for structured bindings? How else can they improve readability or even performance of C++ code?

Notes

As it were mentioned in comments, current implementation of structured bindings lacks some features. They are non-variadic and their syntax does not allow to skip aggregate members explicitly. Here one can find a discussion about variadicity.

like image 362
Sergey Avatar asked Aug 03 '17 09:08

Sergey


3 Answers

Can you provide some other, possibly less obvious use cases for structured bindings? How else can they improve readability or even performance of C++ code?

More in general, you can use it to (let me say) unpack a structure and fill a set of variables out of it:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto [ x, y ] = s;
    (void)x, void(y);
}

The other way around would have been:

struct S { int x = 0; int y = 1; };

int main() {
    S s{};
    auto x = s.x;
    auto y = s.y;
    (void)x, void(y);
}

The same is possible with arrays:

int main() {
    const int a[2] = { 0, 1 };
    auto [ x, y ] = a;
    (void)x, void(y);
}

Anyway, for it works also when you return the structure or the array from a function, probably you can argue that these examples belong to the same set of cases you already mentioned.


Another good example mentioned in the comments to the answer by @TobiasRibizel is the possibility to iterate through containers and unpack easily the contents.
As an example based on std::map:

#include <map>
#include <iostream>

int main() {
    std::map<int, int> m = {{ 0, 1 }, { 2, 3 }};
    for(auto &[key, value]: m) {
        std::cout << key << ": " << value << std::endl;
    }
}
like image 96
skypjack Avatar answered Nov 07 '22 03:11

skypjack


Can you provide some other, possibly less obvious use cases for structured bindings?

They can be used to implement get<N> for structs - see magic_get's automatically generated core17_generated.hpp. This is useful because it provides a primitive form of static reflection (e.g. iterate over all members of a struct).

like image 10
Vittorio Romeo Avatar answered Nov 07 '22 02:11

Vittorio Romeo


Barring evidence to the contrary, I think Structured Bindings are merely a vehicle to deal with legacy API. IMHO, the APIs which require SB should have been fixed instead.

So, instead of

auto p = map.equal_range(k);
for (auto it = p.first; it != p.second; ++it)
    doSomethingWith(it->first, it->second);

we should be able to write

for (auto &e : map.equal_range(k))
    doSomethingWith(e.key, e.value);

Instead of

auto r = map.insert({k, v});
if (!r.second)
    *r.first = v;

we should be able to write

auto r = map.insert({k, v});
if (!r)
    r = v;

etc.

Sure, someone will find a clever use at some point, but to me, after a year of knowing about them, they are still an unsolved mystery. Esp. since the paper is co-authored by Bjarne, who's not usually known for introducing features that have such a narrow applicability.

like image 2
Marc Mutz - mmutz Avatar answered Nov 07 '22 04:11

Marc Mutz - mmutz