why std::move behaves like std::copy? [duplicate] - c++

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.

Related

C++20-ranges: reuse of a filter view

I do not understand the result of the following code (C++20 compiled with g++-10):
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
int main() {
using namespace std;
auto v = vector<int>{0,1,2,3,4,5,6,7};
auto r = v | views::filter([](int n) {return n % 3 == 0;});
ranges::generate(r,[]() { return 2; });
auto s = v | views::filter([](int n) {return n % 3 == 0;});
cout << "v: ";
for(auto x: v) cout << x << " ";
cout << "\n";
cout << "r: ";
for (auto x: r) cout << x << " " ;
cout << "\n";
cout << "s: ";
for (auto x: s) cout << x << " " ;
cout << "\n";
}
On my machine, the result is:
v: 2 1 2 2 4 5 2 7
r: 2
s:
I understand the first (v) and the third lines (s): in v, integers divisible by 3 are replaced by 2 (first line) and thus afterwards no integer is divisible by 3 anymore. Thus the third line is empty.
But why does the second line (r) display a unique value 2 and is different from the third line ???
If I replace the 2 by 3 in the ranges::generate then the result is normal:
v: 3 1 2 3 4 5 3 7
r: 3 3 3
s: 3 3 3
Maybe the reason is trivial but I did not find any reason in the documentation...
You are violating [range.filter.iterator]/1:
Modification of the element a filter_­view​::​iterator denotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.
Your predicate here is elements divisible by 3, and you're modifying all of those to be 2 - which does not satisfy the predicate. That's undefined behavior. Don't do that.
In your followup example, you're modifying elements, but they still satisfy the predicate - so this is fine.
The reason this is the case is that filter has to cache its begin() iterator to satisfy the amortized O(1) constant requirement. What ends up happening is that during the generate, the filter looks for the first element that satisfies the predicate (which in this case is the very first element, the 0). That result is cached.
You then overwrite that value to 2, which is no longer divisible by 3, but the filter's begin() still refers to it. Which is why it gets printed.
When you create a new filter, that one doesn't have its begin() cached yet so there's no such issue.

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

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.

Why does the value assigned by get<>() in my code change outside the construct?

Why is the output of statement 2 different from that of statement 1?
// a is of type vector < tuple <int, int> >
for (auto i: a)
{
get<0>(i)+=get<1>(i);
cout << get<0>(i) << " " << get<1>(i) << endl; // 1
}
for (auto i: a) cout << get<0>(i) << " " << get<1>(i) << endl; // 2
Suppose that initially, a contains [7, 3] , [9, 1]
Then 1 outputs
10 3
10 1
whereas 2 outputs
7 3
9 1
In short, the loop enclosing statement 1 seems to have no effect.
I think it has something to do with my usage of auto and not using *i to change the value, but I don't think that we can use *i in get.
Because when you do:
for (auto i: a)
you're making copies of every element in a. You're iterating by value and modifying local copies of the elements of the container. If you want to actually modify elements of the container, you have to iterate by reference:
for (auto& i : a)
^^^
auto does not deduce reference.
Yes, your suspicion is correct, in for (auto i: a) the i contains a copy of the tuple. Any changes affects the copy only.
You can use for (auto& i: a) to make changes that affects the "original" tuple.

Modify the vector class in STL c++, is this possible? [duplicate]

This question already has answers here:
C++ Erase vector element by value rather than by position? [duplicate]
(4 answers)
Closed 7 years ago.
I would like to modify a member function of the vector class. Is this possible? For example I would like to be able to delete with respect to value, and not the integer of the vector.
I think this does what you are looking for. This example removes all the occurrences of the number 6 from a vector using std::remove. xs.erase is to make sure the vector removes the elements and shrinks the vector to the new size.
I generally avoid modifying the STL containers as the people who implemented them are likely far smarter than me when it comes to this kind of thing. I recommend you learn the standard library algorithms, there generally is one suited to most kinds of general container operations.
#include <iostream>
#include <algorithm>
int main(int argc, char** argv)
{
std::vector<int> xs{1, 3, 6, 2, 6, 5};
std::cout << "xs size: " << xs.size() << std::endl; // xs.size() == 6
auto value_to_remove = 6;
xs.erase(std::remove(xs.begin(), xs.end(), value_to_remove), xs.end());
for (const auto& x : xs)
std::cout << x << ", ";
std::cout << std::endl; // 1, 3, 2, 5,
std::cout << "xs size: " << xs.size() << std::endl; // xs.size() == 4
return 0;
}
In this case, as with most other STL algorithms value_to_remove can be replaced with a (unary) lambda predicate opening up a whole world of exotic value finding.
You could wrap the above in a function as follows:
template <typename T>
void erase_by_value(std::vector<T>& xs, const T& value_to_remove)
{
xs.erase(std::remove(xs.begin(), xs.end(), value_to_remove), xs.end());
}

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.