c++ why is move/forward necessary in moving constructor [duplicate] - c++

This question already has answers here:
Why do you use std::move when you have && in C++11? [duplicate]
(4 answers)
Closed 3 years ago.
After constructing an object with a moving constructor, the new object should "steal" the resources of the "source" object, which is then left in an indefinite (but valid) state.
For example:
#include <iostream>
#include <vector>
template <class T>
void print(const std::vector<T>& v)
{
std::cout << "size = " << v.size() << " vector = ";
for (const auto& x : v)
std::cout << x << " ";
std::cout << std::endl;
}
int main()
{
std::vector<int> data(10, 3);
std::cout << "data:" << std::endl;
print(data);
std::vector<int> data2(std::move(data));
std::cout << "data2:" << std::endl;
print(data2);
std::cout << "data after moving:" << std::endl;
print(data);
return 0;
}
See it live on Coliru.
As far as I know the standard does not specify the content of data after having called the moving constructor, however one would expect that the resources of data have been stolen by data2. And indeed, the output the program above shows that:
data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
data2:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
data after moving:
size = 0 vector =
Now consider a slight variation of the above program:
#include <iostream>
#include <vector>
class A {
std::vector<int> m_data;
public:
A(std::vector<int>&& data) : m_data{data} { }
const std::vector<int>& data() const { return m_data; }
};
template <class T>
void print(const std::vector<T>& v)
{
std::cout << "size = " << v.size() << " vector = ";
for (const auto& x : v)
std::cout << x << " ";
std::cout << std::endl;
}
int main()
{
std::vector<int> data(10, 3);
std::cout << "data:" << std::endl;
print(data);
A x{std::move(data)};
std::cout << "x.data():" << std::endl;
print(x.data());
std::cout << "data after moving:" << std::endl;
print(data);
return 0;
}
See it live on Coliru.
I am surprised by output of the program:
data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
x.data():
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
data after moving:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
It looks like the vector data has been just copied to A::m_data rather then being moved.
If I substitute the moving constructor of A with
A(std::vector<int>&& data) : m_data{std::move(data)} { }
(see it live on Coliru)
or with
A(std::vector<int>&& data) : m_data{std::forward<std::vector<int>&&>(data)} { }
(see it live on Coliru)
then the output of the program resembles that of the first code
data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
x.data():
size = 10 vector = 3 3 3 3 3 3 3 3 3 3
data after moving:
size = 0 vector =
In other words, it seems that either std:move or std::forward are necessary to effectively call the moving constructor of A::m_data. Both std::move and std::forward return a static_cast to std::vector<int>&&, but the argument of the moving constructor of A is already a rvalue.
Why is an additional std::move or std::forward necessary?

First your should know that A::A(std::vector<int>&& data) means that data is rvalue reference to std::vector<T>, just means reference not a call to move constructor,
and A(std::vector<int>&& data) : m_data{data} { } just calls copy constructor of m_data because data itself is a named value not a r value temporary in scope of A::A hence a std::move or std::forward is necessary.

Related

How reinterpret_cast works for flattening a std::vector?

I would like to represent a std::vector of a structure containing several integers as a "flatten" vector of integers, without copying the data.
I tried something with a reinterpret_cast as shown below:
#include <vector>
#include <iostream>
struct Tuple
{
int a, b, c;
};
int main()
{
// init
std::vector<Tuple> vec1(5);
for(size_t i=0; i<vec1.size(); ++i)
{
vec1[i].a = 3 * i + 0;
vec1[i].b = 3 * i + 1;
vec1[i].c = 3 * i + 2;
}
// flattening
std::vector<int>* vec2 = reinterpret_cast<std::vector<int>*>(&vec1);
// print
std::cout << "vec1 (" << vec1.size() << ") : ";
for(size_t i=0; i<vec1.size(); ++i)
{
std::cout << vec1.at(i).a << " " << vec1.at(i).b << " " << vec1.at(i).c << " ";
}
std::cout << std::endl;
std::cout << "vec2 (" << vec2->size() << ") : ";
for (size_t j = 0; j < vec2->size(); ++j)
{
std::cout << vec2->at(j) << " ";
}
std::cout << std::endl;
return 0;
}
which works well since the output is:
vec1 (5) : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
vec2 (15) : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
My questions are :
Is this behavior compiler dependent? (I am using g++ 6.3.0)
How vec2 knows that the size of the vector is 15 and not 5?
Is their any other solution avoiding the use of reinterpret_cast? (If I "accidentally" add a double member to Tuple, the resulting issue could be hard to track...)
If vec1 has a specific allocator: std::vector<Tuple,A<Tuple>>, what should be the type of vec2? std::vector<int> or std::vector<int,A<int>> or std::vector<int,A<Tuple>>?
You can't legally reinterpret_cast the entire vector to a different type of vector. But you can legally cast a pointer to struct to a pointer to the first element of that struct. So this works:
std::vector<Tuple> vec1(5);
int* vec2 = &vec1.front().a;
size_t vec2_size = vec1.size() * sizeof(vec1[0]) / sizeof(vec2[0]);
for (size_t j = 0; j < vec2_size; ++j)
{
std::cout << vec2[j] << " ";
}
You need to make sure there's no padding in Tuple, so:
static_assert(sizeof(Tuple) == 3 * sizeof(int), "Tuple must be 3 ints");
To answer your bulleted questions:
Is this behavior compiler dependent?
Your code was illegal.
How vec2 knows that the size of the vector is 15 and not 5?
You got lucky, your code was illegal.
Is their any other solution avoiding the use of reinterpret_cast?
See above.
If vec1 has a specific allocator: std::vector>, what should be the type of vec2?
Same as above, int*.

Simple C++ Program Compiled without Optimization by GCC doesn't Generate Expected Results

Today when I tried to compile a very simple C++ program using GCC7, I met a very strange problem: the program didn't add any elements to a vector in the constructor, when compiled without optimization (e.g. -O0/-Og) by the GCC 7.2.1 from Devtoolset-7 on Red Hat Enterprise Linux 7. Only when the optimization switch was added (e.g. -O/-O1/-O2/...), the compiled binary can generated expected results. But why is this happening?
By the way:
without optimization, the binaries compiled by GCC 7.2.1 on RHEL7 and GCC 7.3.0 on Mac (Homebrew version) behaved differently: the former didn't add any elements, while the latter add 2 elements.
clang doesn't have this problem no matter the optimization is turned on or not)
The code:
#include <vector>
#include <utility>
#include <iostream>
class Container
{
std::vector<std::size_t> elements;
public:
Container() {}
Container(std::size_t n)
{
std::cout << "Creating " << n << " elements:";
for(int i; i<n; ++i)
{
std::cout << " " << i+1;
elements.push_back(i+1);
}
std::cout << '\n';
}
Container(Container& c) : elements{c.elements} {}
Container(Container&& c) : elements{std::move(c.elements)} {}
virtual ~Container() noexcept {}
Container& operator=(const Container& c)
{
if(this != &c)
{
elements = c.elements;
}
return *this;
}
Container& operator=(Container&& c)
{
if(this != &c)
{
elements = std::move(c.elements);
}
return *this;
}
void print()
{
std::cout << "Container has " << elements.size() << " elements:" << '\n';
for(auto it=elements.cbegin(); it!=elements.cend(); ++it)
{
if(it == elements.cbegin()) std::cout << *it;
else std::cout << ", " << *it;
}
if(elements.size()>0) std::cout << '\n';
}
};
Container makeContainer()
{
std::cout << "Inside makeContainer()" << '\n';
std::cout << "Before:" << '\n';
Container c(3);
c.print();
std::cout << "Temporary:" << '\n';
Container c_tmp(3);
c_tmp.print();
c = c_tmp;
std::cout << "After:" << '\n';
c.print();
return c;
};
int main()
{
Container c = makeContainer();
std::cout << "Inside main()" << '\n';
c.print();
return 0;
}
Expected output:
Inside makeContainer()
Before:
Creating 3 elements: 1 2 3
Container has 3 elements:
1, 2, 3
Temporary:
Creating 3 elements: 1 2 3
Container has 3 elements:
1, 2, 3
After:
Container has 3 elements:
1, 2, 3
Inside main()
Container has 3 elements:
1, 2, 3
Actual output:
Inside makeContainer()
Before:
Creating 3 elements:
Container has 0 elements:
Temporary:
Creating 3 elements:
Container has 0 elements:
After:
Container has 0 elements:
Inside main()
Container has 0 elements:
If you do not assign a value to a variable its state is indeterminate.
In debug mode the compiler can put the value zero to initialize indeterminate values to help with debugging. But in release this extra unasked for initialization will not happen.
for(int i; i<n; ++i) // Here you have declared `i` but not initialized it.
As a result in release mode the value is probably larger than n and thus no elements are inserted.
Note: It is UB to read the value of an initialized variable (so your whole program can do anything).

why std::move behaves like std::copy? [duplicate]

This question already has answers here:
Are std::move and std::copy identical?
(2 answers)
Closed 6 years ago.
Consider the following piece of code -
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> d {100, 200, 300};
std::vector<int> l {1, 2, 3, 4, 5};
std::move(d.begin(), d.end(), std::inserter(l, l.begin()));
for (int n : l) std::cout << n << ' ';
std::cout << '\n';
for (int n : d) std::cout << n << ' ';
std::cout << '\n\n';
for (int &n : d) n +=5;
for (int n : l) std::cout << n << ' ';
std::cout << '\n';
for (int n : d) std::cout << n << ' ';
std::cout << '\n';
}
Here the original std::vector l was inserted with contents of std::vector d after the move operation. I know that all standard library objects that have been moved from are placed in a valid but unspecified state, however, I was curious to move further and inspect the values. The value of std::vector d still remained same after the move operation which could be justified as if both are referring to same data location? Again, when I tried to modify those values, the change is not reflected back in the new std::vector l container.
Here's the output -
100 200 300 1 2 3 4 5
100 200 300
100 200 300 1 2 3 4 5
105 205 305
It seems the values are copied from source container to destination container and the source container gets to keep originals. Doesn't this sound like a std::copy operation?
For plain old data, moving and copying are identical. There's no way to move ordinary data other than by copying. Things are different if, for example, you have ownership of some other object that can be transferred without copying it (like std::string or std::shared_ptr has). But for int, that doesn't apply.

Why std::make_move_iterator works on vector<string> but not on vector<int>

I was expecting that std::make_move_iterator will always move contents, but it seems not.
It looks like it is moving elements in vector<string> but not in vector<int>.
See the below code snippet:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
void moveIntVector()
{
std::cout << __func__ << std::endl;
std::vector<int> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(i);
}
std::vector<int> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
void moveStringVector()
{
std::cout << __func__ << std::endl;
std::vector<std::string> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(std::to_string(i));
}
std::vector<std::string> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
int main()
{
moveIntVector();
moveStringVector();
return 0;
}
The result is:
moveIntVector
v1 is: 0 1 2 3 4 5 6 7 8 9 # I expect this should be `0 1 2 3 4` as well!
v2 is: 5 6 7 8 9
moveStringVector
v1 is: 0 1 2 3 4
v2 is: 5 6 7 8 9
I'm on Ubuntu 14.04, gcc 4.8.2 and the code is compiled with -std=c++11
Could you explain why std::make_move_iterator have different behaviour on vector<int> and vector<string>? (Or is it a bug?)
The behaviour is expected. A move from both vectors leaves the original v1 with 5 moved-from elements in their second half.
The difference is that when the strings are moved, what is left behind is empty strings. This is because it is a very efficient way to move strings, and leave the moved-from string in a self-consistent state (Technically, they could be left to hold the value "Hello, World, nice move!", but that would incur extra cost). The bottom line is that you don't see those moved-from strings in your output.
In the case of the int vectors, there is no way to move an int that is more efficient than copying it, so they are just copied over.
If you check the sizes of the vectors, you will see the v1 have size 10 in both cases.
Here's a simplified example to illustrate that the moved from strings are left empty:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> v1{"a", "b", "c", "d", "e"};
std::vector<std::string> v2(std::make_move_iterator(v1.begin()),
std::make_move_iterator(v1.end()));
std::cout << "v1 size " << v1.size() << '\n';
std::cout << "v1: ";
for (const auto& s : v1) std::cout << s << " - ";
std::cout << '\n';
std::cout << "v2 size " << v2.size() << '\n';
std::cout << "v2: ";
for (const auto& s : v2) std::cout << s << " - ";
std::cout << '\n';
}
Output:
v1 size 5
v1: - - - - -
v2 size 5
v2: a - b - c - d - e -
When we talk about a move we are not talking about moving the object itself (it remains intact). What gets moved are its internal data. This may or may not affect the value of the object whose internal data gets moved.
That is why your int array doesn't loose its original ints. As to your string example, it still has the original std::strings just like the int example but their internal values have changed to empty strings.
It is important to remember that internally a std::string (essentially) holds a pointer to a character array. So when you copy a std::string you copy every element of the character array. A move, however, avoids doing all that copying by copying the internal pointer instead.
But if the move operation stopped there that would leave both std::strings pointing at the same character array and changing the character data pointed to by either std::string would also change the other's. So when you move a string it is not enough to merely copy the internal pointer, you have to make the internal pointer of the std::string you moved from point to a new blank character array so that it can no longer affect the string its data was moved to.
When moving an int there is no further action required after the copy of its data. There are no pointers involved so after the copy both ints contain independent data.
move constructor is like of an object works like taking a regular reference and a instruction to move things. the default move constructor tries to call the move constructor of all member variables. a user defined one... pretty much it's up to the programmer to tell it what to do.
you could program your objects to be in a undefined state after being subject to a move constructor, you can keep them unchanged(the destructor will still be called so you need to take care of that), you can keep them valid. strings will have a defined state after being subject to a move constructor.
as for your example...
int is trivially copyable and it's move constructor won't do anything but copying.
string is not trivially copyable. it has some dynamic stuff in it that the move constructor moves. and the previous one is left with a length of zero, you ARE printing them, along with the trailing "space" which you added. it's just they are the last 5 elements, at the end of what your printing and you aren't noticing it because it's equivalent to 5 trailing white spaces.

Using Template Specialization To Discriminate Between Values And Arrays (of Values)

I am trying to construct a device which will do one thing for a value (specifically, a native type) and another thing for a C-style array of primitives.
This is what I have now, which does not do what I wish.
#include <cstdlib>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
template<class V> void dump_buf(const V& val)
{
cout << "val = " << val << "\n";
}
template<class A> void dump_buf(const A ary[])
{
cout << "ary size = " << sizeof(ary) << "\n";
for( size_t i = 0; i < sizeof(ary); ++i )
cout << "\t" << i+1 << " : " << ary[i] << "\n";
}
int main()
{
cout << "\n";
int i = 42;
float f = 3.14f;
unsigned fib[] = {0,1,1,2,3,5};
char s[] = "hello";
dump_buf(i);
dump_buf(f);
dump_buf(s);
dump_buf(fib);
}
This doesn't work because ary is of type pointer-to-something, not array-of-something. The output for the above (Intel, x64 compile, VS9) is:
val = 42
val = 3.14
ary size = 8
1 : h
2 : e
3 : l
4 : l
5 : o
6 :
7 : ╠
8 : ╠
ary size = 8
1 : 0
2 : 1
3 : 1
4 : 2
5 : 3
6 : 5
7 : 3435973836
8 : 3435973836
But I want the output to be:
val = 42
val = 3.14
ary size = 6
1 : h
2 : e
3 : l
4 : l
5 : o
6 :
ary size = 6
1 : 0
2 : 1
3 : 1
4 : 2
5 : 3
6 : 5
Note that the desired output for the string is 6 characters rather than 5 because of the null-terminator, which is part of the array.
Is there any way to get this to work, using only Standard C++ and no additional libraries?
Nearly any Standard-conformant technique would be acceptable. Overloading, template specialization, overriding, class templates... all are OK. I'm wide open to almost any technique that will accomplish my goal.
This should do the trick:
template<class A, size_t S> void dump_buf(const A (& ary)[S])
{
cout << "ary size = " << S << "\n";
for( size_t i = 0; i < S; ++i )
cout << "\t" << i+1 << " : " << ary[i] << "\n";
}
By taking a reference to an array, rather than a pointer to the start of an array, the size is known and available as an inferred template argument.
Also, remember that sizeof gives the size in bytes, so when you do want the number of objects in an array (and can't or don't want to use a template like this), you want sizeof(ary)/sizeof(*ary). In both cases, you need a real array, not a pointer to an array, which has lost all knowledge of the array's size.