[Boost.Range] combine, native range-for, and destructuring
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple. boost::combine is a function for that. for (auto&[def,residue] : boost::combine(playdef, residues)) { gives me an error 1>e:\scratch\c++code\scratchproject2\fizzbuzz\fizzbuzz.cpp(66): error C2440: 'initializing': cannot convert from 'boost::tuples::cons<std::pair<int,std::string_view> &,boost::tuples::cons<int &,boost::fusion::detail::build_tuple_cons<boost::fusion::single_view_iterator<Sequence,boost::mpl::int_<1>>,Last,true>::type>>' to 'boost::tuples::cons<std::pair<int,std::string_view> &,boost::tuples::cons<int &,boost::fusion::detail::build_tuple_cons<boost::fusion::single_view_iterator<Sequence,boost::mpl::int_<1>>,Last,true>::type>> &' The only difference is in the final `&` character. Trying to explore the behavior in pieces, I try: for (auto row : boost::combine(playdef, residues)) { auto&[def, residue] = row; // line 1 row.get<1>() = 17; // line 2 residue= 42; // line 3 Now line2 does work, and I note that assignment goes through and updates the original `residues` vector. The destructuring on line 1 does compile now, being on a separate line. But line 3 gives me an error about not being able to assign to a crazy complicated type. What's going on? Is Boost.Range zipped ranges simply not compatible with the native language features?
On Sat, 14 Apr 2018 at 08:02 John M. Dlugosz via Boost-users < boost-users@lists.boost.org> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple. boost::combine is a function for that.
for (auto&[def,residue] : boost::combine(playdef, residues)) {
gives me an error
<snip>
Trying to explore the behavior in pieces, I try:
for (auto row : boost::combine(playdef, residues)) { auto&[def, residue] = row; // line 1 row.get<1>() = 17; // line 2 residue= 42; // line 3
Now line2 does work, and I note that assignment goes through and updates the original `residues` vector.
The destructuring on line 1 does compile now, being on a separate line. But line 3 gives me an error about not being able to assign to a crazy complicated type.
What's going on? Is Boost.Range zipped ranges simply not compatible with the native language features?
Hi John, I'm not en expert on this library, but I have run into the first of your problems before. When dereferencing a normal iterator, the iterator returns a reference to the actual object being pointed to. The zip iterator can not do this, instead it returns a tuple of references to the the underlying objects as a temporary. Consider: vector<int> i; vector<float> j; *boost::range::combine(i, j); // This must return something like tuple<int&, float&> So, you need to bind whats returned from combine to either a const& or an rvalue ref. for (auto&& row : boost::combine(playdef, residues)) or for (auto const& row : boost::combine(playdef, residues)) Even the const will work because it's the tuple that's const, not the objects being referenced. As an aside, this means that zip_iterator is not, and can not be, a "proper" iterator as far as standard C++ is concerned. If I remember correctly, this is one issue that the ranges proposal is trying to fix. As for the second problem, this looks to me like a bug as the library is not correctly interacting with C++17 structured bindings. This should not really be a surprise, considering this library is much older than that language feature, but it should be addressed, so log a bug, or have a go at trying to fix it and submit a pull request. Hope this helps, -- chris
On 14 April 2018 at 04:34, John M. Dlugosz via Boost-users < boost-users@lists.boost.org> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple. boost::combine is a function for that.
I've run into this problem as well, it's a real shame it doesn't work, because if you would be able to use boost::combine with (C++17) structured bindings, one can completely abstract away (if it would work) the zip-iterator (you wouldn't see it even though that's what's happening under the hood)... degski PS: I did not report it because I wasn't sure the problem was not related to the MS-implementation of structured bindings (this is a while ago), and I did not want to attract anymore flak for being a ms-boy. It seems it isn't then.
On 14 April 2018 at 17:49, degski <degski@gmail.com> wrote:
On 14 April 2018 at 04:34, John M. Dlugosz via Boost-users < boost-users@lists.boost.org> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple. boost::combine is a function for that.
I've run into this problem as well, it's a real shame it doesn't work, because if you would be able to use boost::combine with (C++17) structured bindings, one can completely abstract away (if it would work) the zip-iterator (you wouldn't see it even though that's what's happening under the hood)...
The construct does work with std:tie, but that's at least 3 more lines of code and does not look as "sexy" as the (not working) structured bindings approach (sorry for the nerdy "does not look sexy" comment). degski
John M. Dlugosz via Boost-users wrote:
for (auto&[def,residue] : boost::combine(playdef, residues)) {
gives me an error
This fails, because the deref operator of zip_iterator generates a prvalue (of a tuple of references). Instead, you can use - for (auto [x, y] : combine(...)) {...} - for (auto const& [x, y] : combine(...)) {...} - for (auto&& [x, y] : combine(...)) {...}
for (auto row : boost::combine(playdef, residues)) { auto&[def, residue] = row; // line 1 row.get<1>() = 17; // line 2 residue= 42; // line 3
Now line2 does work, and I note that assignment goes through and updates the original `residues` vector.
The destructuring on line 1 does compile now, being on a separate line. But line 3 gives me an error about not being able to assign to a crazy complicated type.
Hmm..., with boost (develop branch) + GCC 7.3 or Clang trunk, the following code runs fine: std::vector<int> v1{1, 1, 1}; std::vector<int> v2{2, 2, 2}; for (auto&& tpl : boost::range::combine(v1, v2)) { auto [x, y] = tpl; // `auto&`, `auto const&` and `auto&&` also work fine x = 10; y = 20; } for (auto&& [x, y] : boost::range::combine(v1, v2)) { x = 200; y = 200; } Regards, Michel
On Tue, 17 Apr 2018 at 11:00 Michel Morin via Boost-users < boost-users@lists.boost.org> wrote:
Hmm..., with boost (develop branch) + GCC 7.3 or Clang trunk, the following code runs fine:
std::vector<int> v1{1, 1, 1}; std::vector<int> v2{2, 2, 2};
for (auto&& tpl : boost::range::combine(v1, v2)) { auto [x, y] = tpl; // `auto&`, `auto const&` and `auto&&` also work fine x = 10; y = 20; }
for (auto&& [x, y] : boost::range::combine(v1, v2)) { x = 200; y = 200; }
FWIW, I just checked, and this works in the develop, but not master or 1.67. -- chris
participants (4)
-
Chris Glover
-
degski
-
John M. Dlugosz
-
Michel Morin