Igor
Igor

Reputation: 1317

Using std::move with vectors

I have a question about using std::move in C++.

Let's say I have the following class, which in its constructor takes a std::vector as a parameter:

class A
{
public:
    A(std::vector<char> v): v(v) {}
private:
    std::vector<char> v;
};

But if I write the following somewhere:

std::vector<char> v;
A a(v);

the copy constructor of std::vector will be called twice, right? So should I write constructor for A like the following?

class A
{
public:
    A(std::vector<char> v): v(std::move(v)) {}
private:
    std::vector<char> v;
};

And what if I would like to call the following?

std::vector<char> v;
A a(std::move(v));

Is that okay with the second version of the constructor, or should I create another constructor for A that takes std::vector<char>&&?

Upvotes: 6

Views: 12429

Answers (4)

Klaus
Klaus

Reputation: 25593

You should write a move constructor!

A::A( std::vector<char>&& vec): v(std::move(vec) {}

If you did not write that, you get one more copy here.

You have to keep in mind that your vector will be moved "away". So if you write something like:

std::vector<char> v;
A a(std::move(v));

so your vector v should not used after the call of constructor of A. All elements are moved away and iterators to the content are invalid. The vector can be used to store new data.

So if you write

std::vector<char> v;
A a(v);

and this will result in a move like in your example, nobody would expect the move.

Upvotes: 2

A.S.H
A.S.H

Reputation: 29332

Make two constructors: The first constructor should be by const&, the second as rvalue&& so it can use move semantics:

class A
{
public:
    A(const std::vector<char>& v_) : v(v_) { std::cout << "const& ctor\n"; }
    A(std::vector<char>&& v_) : v(std::move(v_)) { std::cout << "rvalue&& ctor\n"; }
private:
    std::vector<char> v;
};

Test:

int main()
{
    std::vector<char> v1{ 'a', 'b', 'c', 'd' };
    A a1(v1);
    A a2(std::vector<char>{ 1, 2, 3, 4 });
   return 0;
}

Output:

const& ctor

rvalue&& ctor

Upvotes: 5

Your second scheme is fine.

The vector will just be moved twice with the second version (which moves the by-value parameter into your member). If you don't mind the cost of the extra move, you can stick with just moving the by-value parameter into your member.

If you do mind, then make two constructors for different value categories of the parameter.

Upvotes: 5

SergeyA
SergeyA

Reputation: 62553

Many times with vectors you would be fine by accepting vector by value and moving from it. So,

A(std::vector<char> v): v(std::move(v)) {}

However, this assumes that you are only calling it for 'natural' lvalues or temporaries. Meaning, it would be performant for cases like

A a(std::vector<char>{....}); // <--- 1
std::vector<char> my_vec;
A b(my_vec); // <--- 2

If you expect to call it with an lvalue which you'd like to be moved from, like in your example, than you need two constructors - with const lvalue reference and rvalue references:

A(const std::vector<char>& v) : v(v) { }
A(std::vector<char>&& v) : v(std::move(v)) { }  

Upvotes: 3

Related Questions