Copy VS Std::Move for Ints

copy vs std::move for ints

In this example, there is no difference. We will end up with 3 ints with value 100. There could definitely be a difference with different types though. For instance, let's consider something like vector<int>:

std::vector<int> a = {1, 2, 3, 4, 5}; // a has size 5
auto a_copy = a; // copy a. now we have two vectors of size 5
auto a_move = std::move(a); // *move* a into a_move

The last variable, a_move, takes ownership of a's internal pointers. So what we end up with is a_move is a vector of size 5, but a is now empty. The move is much more efficient than a copy (imagine if it was a vector of 1000 strings instead - a_copy would involve allocating a 1000-string buffer and copying 1000 strings, but a_move just assigns a couple pointers).

For some other types, one might be invalid:

std::unique_ptr<int> a{new int 42};
auto a_copy = a; // error
auto a_move = std::move(a); // OK, now a_move owns 42, but a points to nothing

For many types, there's no difference though:

std::array<int, 100> a;
auto a_copy = a; // copy 100 ints
auto a_move = std::move(a); // also copy 100 ints, no special move ctor

More generally:

T a;
auto a_copy = a; // calls T(const T& ), the copy constructor
auto a_move = std::move(a); // calls T(T&& ), the move constructor

What is the benefit of moving a range of elements in a vector vs. copying?

Here, I am moving a range of elements from v1 to v2, but is there any
particular advantage to doing this vs. copying?

No. Here is all happened just a range coping, because your usage of std::move for primitive types just do coping. Therefore it does the same as simple if you would have:

std::vector<int> v2{v1.begin() + 2, v1.end()};

Therefore you are correct about the findings. However, it is called fundamental types/ primitive types, not PODs.



But in the former move of a range of elements, I don't see the usefulness.

Consider the case of std::vector</*expensive copy type*/>, in which it makes sense to have a move of the underlying range elements, whenever is possible.

For instance consider the std::vector<std::string> case

std::vector<std::string> v1{ "1","2","3","4","5","6","7" };
std::vector<std::string> v2;
// reserve memory for unwanted reallocations
v2.reserve(std::distance(v1.begin() + 2, v1.end()));

// moves the string element in the range
std::move(v1.begin() + 2, v1.end(), back_inserter(v2));

// v1 now: 1 2
// v2 now: 3 4 5 6 7

(See a demo here)


As a side note, instead of std::move the range in a separate line, for iterators, one could also use std::make_move_iterator, to do the range move construction while declaration (if make sence).

#include <iterator> // std::make_move_iterator

std::vector<std::string> v1{ "1","2","3","4","5","6","7" };
std::vector<std::string> v2{ std::make_move_iterator(v1.begin() + 2),
std::make_move_iterator(v1.end()) };

Cost of copy vs move std::shared_ptr

I wrote a benchmark. On my Macbook Air it is three times faster (g++ as well as clang++ -std=c++17 -O3 -DNDEBUG). Let me know if you see problems with the benchmark.

#include <chrono>
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
using namespace std::chrono;

int COUNT = 50'000'000;

struct TimeIt
{
system_clock::time_point start;
TimeIt() {
start = system_clock::now();
}
~TimeIt() {
auto runtime = duration_cast<milliseconds>(system_clock::now()-start).count();
cout << runtime << " ms" << endl;
}

};

void benchmark_copy(const vector<shared_ptr<int>> &vec_src)
{
cout << "benchmark_copy" << endl;
vector<shared_ptr<int>> vec_dst;
vec_dst.reserve(COUNT);
TimeIt ti;
for(auto &sp : vec_src)
vec_dst.emplace_back(sp);
}
void benchmark_move(vector<shared_ptr<int>> &&vec_src)
{
cout << "benchmark_move" << endl;
vector<shared_ptr<int>> vec_dst;
vec_dst.reserve(COUNT);
TimeIt ti;
for(auto &sp : vec_src)
vec_dst.emplace_back(move(sp));

}

int main (int arg, char **argv){

vector<shared_ptr<int>> vec;
for (int i = 0; i < COUNT; ++i)
vec.emplace_back(new int);

benchmark_copy(vec);
benchmark_move(move(vec));

}

Copying vs Moving while casting objects

You don't need to write the copy and move constructors as the compiler will implicitly generate them if the only behavior is to copy/move the std::vector member.

By the way, you may want to implement your ColumnVector as a view of the Vector (something like std::string_view) so that there is even no move.

Why does std::move copy contents for a rvalue or const lvalue function argument?

Your vector is actually copied, not moved. The reason for this is, although declared as an rvalue reference, vec_ denotes an lvalue expression inside the function body. Thus the copy constructor of std::vector is invoked, and not the move constructor. The reason for this is, that vec_ is now a named value, and rvalues cannot have names, so it collapses to an lvalue. The following code will fail to compile because of this reason:

void foo(int&& i)
{
int&& x = i;
}

In order to fix this issue, you have to make vec_ nameless again, by calling std::move(vec_).

why std::move behaves like std::copy?

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 Topics



Leave a reply



Submit