I am new to move semantics and want to understand what's happening in this piece of code:
vector<int> create_vector() {
return {0, 1, 2};
}
for (int &x : create_vector()) {
/* ... */
}
If I understand correctly, the function call create_vector() returns an rvalue. Therefore, the elements inside it should be rvalues as well (i.e. 0, 1, 2 are rvalues). However, in the range for loop, we are binding these rvalues to a non-const lvalue reference int &x, which shouldn't be allowed? I thought non-const lvalue references can only bind to lvalues, not rvalues.
What's happening here? Why is this allowed?
You're returning the Container with rvalue, but the individual elements are being returned after calling ::begin() on the vector and then calling the operator*() on the returned iterator which returns a reference as you can see here:
// Forward iterator requirements
_GLIBCXX20_CONSTEXPR
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *_M_current; }
If you write the for loop in the cppinsights.io then you'll see that the for loop is calling .begin and then it's calling the operator*():
#include <vector>
using namespace std;
int foo() {
int res;
for (auto& val : vector<int>{}) {
res += val;
}
return res;
}
Here's the link that produces this from the above example:
#include <vector>
using namespace std;
int foo()
{
int res;
{
std::vector<int, std::allocator<int> > && __range1 = std::vector<int, std::allocator<int> >{};
__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > __begin1 = __range1.begin();
__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > __end1 = __range1.end();
for(; !__gnu_cxx::operator==(__begin1, __end1); __begin1.operator++()) {
int & val = __begin1.operator*();
res = res + val;
}
}
return res;
}
Here's the vector::begin on cppreference; and here's a link to itrator_traits which can be used to get the reference/pointer type of vector's iterator like this:
using your_vector = std::vector<int>;
using vector_iterator = typename your_vector::iterator;
using traits = std::iterator_traits<vector_iterator>;
using reference_type = typename traits::reference;
using pointer_type = typename traits::pointer;
There are easier ways to get these types but it's the universal way for standard containers.
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