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.
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;
}
}
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
).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With