uk4321
uk4321

Reputation: 1078

Should you move an automatic copy into a member or leave it alone?

If you are making a copy of something to store in a class, like this:

struct S {
    S(std::vector<int> v);
private:
    std::vector<int> m; // gonna store v in here
};

I have heard you're supposed to let the compiler make the copy for you by taking v by value because it can do it more efficiently sometimes. However, in the member initialization list of the constructor, should you initialize m with v or std::move(v)? What if the type of v is a move-only type? (In the latter case I assume you must use move.)

Upvotes: 1

Views: 68

Answers (1)

user2428400
user2428400

Reputation:

If it's move-only, then yes your best course of action is to take the argument by value, then move.

When the type of v could be copyable or movable, I'd describe the state of the "best practice" as somewhat fluid and there are different schools of thought at the moment. But this, I think, is being accepted as the way to write optimal move-aware constructors such as you require.

I would say, do what you've done here, and yes initialise the member from an rvalue using a move cast.

When you USE the constructor, try and take advantage of copy-elision. For example, this:

struct S {
    S(std::vector<int> v) : m(std::move(v)) {}
private:
    std::vector<int> m;
};

std::vector<int> create_vector()
{
    std::vector<int> vec = // initialisation stuff

   return vec;
}

int main()
{
    S instance_of_s{create_vector()};
    return 0;
}

... should ideally not copy anything at all due to various copy-elisions. But of course, this is compiler dependent.

Here's an example which demonstrates this.

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

class VectorWrapper
{
public:
    VectorWrapper() : vec(){}

    VectorWrapper(const VectorWrapper& other) : vec(other.vec){ cout << "COPY" << endl; }

    VectorWrapper(VectorWrapper&& other) : vec(move(other.vec)){ cout << "MOVE" << endl; }

private:
    vector<int> vec;
};

struct S 
{
    S(VectorWrapper v) : m(move(v)) {}
private:
    VectorWrapper m;
};

int main()
{
    S instance_of_s{VectorWrapper()};
    return 0;
}

Under GCC 4.7.2 the output is:

MOVE

References:

http://cpptruths.blogspot.co.uk/2012/03/rvalue-references-in-constructor-when.html

http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

(Note that this is a series of articles, I missed the link for that to start with!)

Upvotes: 1

Related Questions