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).
Initializing multiple variables of different types in an if statement; for instance,
if (auto&& [a, b] = std::pair { std::string { "how" }, 4U }; a.length() < b)
std::cout << (a += " convenient!") << '\n';
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.
Related
This question already has answers here:
Obtaining item index in ranged based for on vector
(2 answers)
Closed 5 years ago.
I have the following loop in my code:
for (const auto& item : vec) {...}
In order to find the position of item in vec, I do:
size_t pos = ((size_t)&item-(size_t)vec.data())/sizeof(item);
But it feels kinda "C" (in opposed to "C++").
I know that I can achieve this purpose if I change my loop-format to either one of the following:
for (size_t i = 0; i < vec.size(); i++) {...}
for (auto it = vec.begin(); it != vec.end(); it++) {...}
But I would like to refrain from doing this.
Bottom line questions:
Is my method guaranteed by the language standard?
Is there a more "C++" way to do this without changing the loop-format?
Thank you.
But I would like to refrain from doing this.
Looping over an index/iterator is the exact thing you need here. Why would you refrain from doing that?
Is my method guaranteed by the language standard?
Just the fact that you have to ask this question to makes your method undesirable compared to a index/iterator loop. Regardless, I cannot see any reason why your method should fail.
Is there a more "C++" way to do this without changing the loop-format?
The most idiomatic C++ way is looping over an index/iterator. If you want a fancier version of that, you can wrap the logic in an higher-order function that provides the item as well:
template <typename Container, typename F>
void for_each_with_index(Container&& c, F&& f)
{
for(std::size_t i = 0; i < c.size(); ++i)
{
f(c[i], i);
}
}
Usage:
std::vector<item> v{/* ... */};
for_each_with_index(v, [](auto& item, std::size_t index)
{
/* ... */
});
live example on wandbox
You don't need to cast your types as you did in your example:
size_t pos = ((size_t)&item-(size_t)vec.data())/sizeof(item);
You can do pointer arithmetics more simplier as follows:
#include <iostream>
#include <vector>
int main() {
std::vector<int> test { 0, 1, 2, 3, 4, 5, 6 };
for(const auto& elem : test) {
const auto pos = &elem - test.data();
std::cout << "Element at position " << pos << " is: " << elem << std::endl;
}
return 0;
}
The reason it works because the type of &elem is const int* and the returning type of test.data() is int* so the pointer arithmetics works fine on them.
Note: if you forget the reference operator (&) in you range-based for loop the example above won't work:
for(const auto elem : test) { /* ... */ } // Warning: elem is a copy!
If you are really dying to do this in C++ without changing the loop format substantially, considering incorporating cpp itertools: https://github.com/ryanhaining/cppitertools/blob/master/README.md. It's your only hope!
vector<int> vec{2, 4, 6, 8};
for (auto&& e : enumerate(vec)) {
cout << e.index
<< ": "
<< e.element
<< '\n';
}
It provides lots more tools, drawing inspiration from python which is known for having very nice ways of iterating. You can also iterate over two same size vectors together, de-nest nested for loops, etc.
Strictly speaking, you can also implement enumerate yourself. It's not that difficult but it is quite a few lines of boilerplate.
Currently I use std::map to save key/value pairs:
#include <map>
using K = int;
struct P {}; // some useful payload
int main()
{
std::map< K, P const > m;
m.insert({1, {}});
auto it = m.find(1);
// access:
it->first;
it->second;
}
int here is just for example. mapped_type is always const in my use case.
To access payload P const I have to use not too informative name second. The same regarding first. I want to name it simply payload or somehow else.
To achieve this, I invent the following approach:
#include <set>
using K = int;
struct P {};
struct A
{
K key;
P payload;
operator K const & () const { return key; }
};
struct less
{
using is_transparent = void;
bool operator () (K const & l, K const & r) const
{
return l < r;
}
};
int main()
{
std::set< A, less > s;
s.insert({1, {}});
auto it = s.find(1);
// access:
it->key;
it->payload;
}
Here I make std::set to use conversion operator every time for the key type. It works. But is it prohibited approach? Is there undefined behaviour?
To me, it looks technically valid.
But it also falls into the category of "making your code so unnecessarily complex that a programmer has to look twice or thrice at it before they comprehend what on earth you're doing, and can validate that you're doing it correctly, and for basically no tangible benefit" which is very ungood.
Indeed, the fact that you — the code's own author, no less! — felt the need to come here asking for a language lawyer to validate the code's correctness is a big red flag that this is likely not worthwhile.
If at any point you feel that .first and .second are insufficiently descriptive for your code, you can instead get around that locally with such magic as:
auto& key = it->first;
auto& payload = it->second;
or even:
auto& payload = getPayload(it);
where getPayload is an appropriate function taking an iterator of your particular type.
These approaches have the benefit of being fairly obvious, and of not requiring convening a session of the C++ Supreme Court to check them over first.
This is a piece of code I'm currently using and I was wondering if there was a more elegant way of doing this in C++11 -
Essentially vector_a is copied to vector_b, then slightly modified, then vector_b is returned.
Vector elements are of class Point which is basically (leaving out constructors and a bunch of methods):
class Point {
double x,
y,
z;
};
Ideally I'd love to boil down the assignment of member z from vector_a to vector_b to something like a line or two but couldn't come up with an elegant way of doing it.
Any suggestions welcome!
auto localIter = vector_a.begin();
auto outIter = vector_b.begin();
while (localIter != vector_a.end() && outIter != vector_b.end())
{
outIter->z = localIter->z;
localIter++;
outIter++;
}
You may use transform().
std::transform (vector_a.begin(), vector_a.end(), vector_b.begin(), vector_a.begin(), [](Elem a, Elem b) { a->z = b->z; return a; });
Where Elem is a type of vector element.
As the vector has a random access iterator (using of std::next is effective) then I would write the code the following way
auto it = vector_a.begin();
std::for_each( vector_b.begin(),
std::next( vector_b.begin(),
std::min( vector_a.size(), vector_b.size() ) ),
[&it] ( Point &p ) { p.z = it++->z; } );
A partial copy is, actually, just a transformation of the elements (one of many), and therefore std::transform is a natural fit here.
Like many algorithms acting on multiple sequences, you have to be careful about the bounds of your containers; in this particular case, since vector_b just receives stuff, the easiest is to start empty and adjust its size as you go.
Thus, in the end, we get:
vector_b.clear();
std::transform(vector_a.begin(),
vector_a.end(),
std::back_inserter(vector_b),
[](Elem const& a) { Elem b; b.z = a.z; return b; });
transform is perhaps the most generic algorithm in the Standard Library (it could imitate copy for example), so you should carefully consider whereas a more specialized algorithm exists before reaching for it. In this case, however, it just fits.
I would be tempted to do something a bit like this:
#include <vector>
struct info
{
int z;
};
int main()
{
std::vector<info> a = {{1}, {2}, {3}};
std::vector<info> b = {{4}, {5}};
for(size_t i(0); i < a.size() && i < b.size(); ++i)
b[i].z = a[i].z;
}
This question already has answers here:
How to use range-based for() loop with std::map?
(5 answers)
Closed 9 years ago.
The new range-based for loops really improve readability and are really easy to use. However, consider the following :
map<Foo,Bar> FooAndAssociatedBars;
for (auto& FooAndAssociatedBar : FooAndAssociatedBars) {
FooAndAssociatedBar.first.doSth();
FooAndAssociatedBar.second.doSomeOtherThing();
}
It may be a detail but I find it would have been more readable if I could have done something like :
for ( (auto& foo, auto& bar) : FooAndAssociatedBars) {
foo.doSth();
bar.doSomeOtherThing();
}
Do you know an equivalent syntax ?
EDIT:
Good news: C++17 has a proposal that adresses this problem, called structured bindings (see 1). In C++17, you should be able to write:
tuple<T1,T2,T3> f(/*...*/) {
/*...*/
return {a,b,c};
}
auto [x,y,z] = f(); // x has type T1, y has type T2, z has type T3
which solves this readability problem
There is no such thing as you want. The closest is to declare variables inside the loop:
for (auto& FooAndAssociatedBar : FooAndAssociatedBars) {
auto& foo = FooAndAssociatedBar.first;
auto& bar = FooAndAssociatedBar.second;
// ...
}
Not a good idea. Sooner or later, you would want the same for a std::tuple, and compiler should be able to use std::get<> on the tuple automatically. In my opinion your approach is pleasing you at the moment only, and you would find problems with this approach (assume it is implemented that way).
Standard committee has designed range-based for-loop with deep consideration. It is way better than foreach loop in other languages, and it is way shorter. Couple it with auto& and you are done!
And of course, you always have the possibility to use lambdas.
std::map<int, const char*> m { { 4, "hello" }, { 11, "c++" } };
convenient_for_each(m, [](int a, const char* b) {
std::cout << b << a << std::endl;
});
convenient_for_each(m, [](std::pair<int, const char> p) {
std::cout << p.first << p.second << std::endl;
});
Or wrapped as macro (not recommended)
FOREACH((int a, const char* b), m, std::cout << a << b << std::endl);
FOREACH((std::pair<int, const char*> p), m, std::cout << p.first << p.second << std::endl);
(Hackish sample implementation at LWS)
Auto won't work though, I'm still waiting for polymorphic lambdas. My approach is theoretically able to handle tuples as well.
The book i am reading offers this example when iterating over a vector
for (auto &e: v) {
cout << e << endl;
}
Suppose v is declared as vector<int> v, in other words, we know that the type of elements inside this collection is int.
Is using auto in any way better or preferred to?
for (int &e: v) {
cout << e << endl;
}
Why?
Yes. auto is preferred. Because if you change the declaration ofv from:
std::vector<int> v; //before
to this:
std::vector<float> v; //after
If you use int & in the for, then you have to change that as well. But with auto, no need to change!
In my opinion, working with auto is more or less like programming to interface. So if you do an operation += in the loop, and you don't really care about the type of the loop variable e as long as the type supports += operation, then auto is the solution:
for(auto & e : v)
{
e += 2;
}
In this example, all you care about that the type of e supports += with int on the right hand side. It will work even for user-defined types, which has defined operator+=(int), or operator+=(T) where T is a type which supports implicit conversion from int . It is as if you're programming to interface:
std::vector<Animal*> animals;
animals.push_back(new Dog());
animals.push_back(new Cat());
animals.push_back(new Horse());
for(size_t i = 0 ; i < animals.size(); ++i)
{
animals[i]->eat(food); //program to interface
}
Of course, you would like to write this loop as:
for(Animal * animal : animals)
{
animal->eat(food); //still program to interface
}
Or simply this:
for(auto animal : animals)
{
animal->eat(food); //still program to interface
}
It is still programming to interface.
But at the same time, the point in #David's comment is worth noting.
On your first example, you have less dependency on what the elements of the vector are.
Suppose that in a month, you require that your vector stores larger integers, so you will have to use an std::vector<int64_t>, or some other, wider type. Now all of the code that iterates over that vector is invalid. You'll have to modify each:
for (int &e: v) {}
For a:
for (int64_t &e: v) {}
That is why it's better to just let auto deduce the internal type. That way you can modify the type stored in your vector for another, compatible one, and all your code will still work.