1

I'm a little confused as to why you would use/need a move constructor.
If I have the following:

vector fill(istream& is)
{
    vector res;
    for(double x; is >> x; res.push_back(x));
    return res;
}

void bar()
{
    vector vec = fill(cin);
    // ... use vec ...
}

I can remove the need to return res, hence not calling the copy constructor, by adding vector fill(istream& is, vector& res).

So what is the point of having a move constructor?

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
Strahinja Ajvaz
  • 2,521
  • 5
  • 23
  • 38
  • Do you think one never ever needs to copy objects in C++? If so, you're doing it wrong. – juanchopanza Dec 02 '15 at 09:32
  • 2
    One reason is that is is much easier to read. If have a function call `somefunction(var1, var2, var3)`, it is not clear if some of them gets modified or not. – VLL Dec 02 '15 at 09:32
  • Have you tried to do some reading on topics of move constructors and move semantics in general? There is an abundance of information, just google it ... – Marko Popovic Dec 02 '15 at 09:34
  • As soon as you do add `vector& res` to `fill()`, you need to construct a `vector` *before* calling `fill()`. You really think that is an improvement? – DevSolar Dec 02 '15 at 09:42
  • @Ville-ValtteriTiittanen: Those parameters that do not get modified should be `const`, shouldn't they? ;-) (Yes, I know, they quite frequently aren't.) – DevSolar Dec 02 '15 at 09:45

3 Answers3

4

Assume you next put you std::vector<T> into a std::vector<std::vector<T>> (if you think vectors shouldn't be nested, assume the inner type to be std::string and assume we are discussing std::string's move constructor): even though you can add an empty object and fill it in-place, eventually the vector will need to be relocated upon resizing at which point moving the elements comes in handy.

Note that returning from a function isn't the main motivator of move construction, at least, not with respect to efficiency: where efficiency matters structuring the code to enable copy-elision further improves performance by even avoiding the move.

The move constructor may still be relevant semantically, though, because returning requires that a type is either copyable or movable. Some types, e.g., streams, are not copyable but they are movable and can be returned that way.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
2

In you example compiler might apply RVO - Return Value Optimization, this means you function will be inlined, so no return will take place - and no move semantics will be applied. Only if it cannot apply RVO - move constructor will be used (if available).

Before move semantics were introduced people were using various techniques to simulate them. One of them is actually returning values by references.

marcinj
  • 48,511
  • 9
  • 79
  • 100
2

One reason is that using assignment operators makes it easier to grasp what each line is doing. If have a function call somefunction(var1, var2, var3), it is not clear whether some of them gets modified or not. To find that out, you have to actually read the other function.

Additionally, if you have to pass a vector as an argument to fill(), it means every place that calls your function will require two lines instead of one: First to create an empty vector, and then to call fill().

Another reason is that a move constructor allows the function to return an instance of a class that does not have a default constructor. Consider the following example:

struct something{
    something(int i) : value(i) {}
    something(const something& a) : value(a.value) {}

    int value;
};

something function1(){
    return something(1);
}

void function2(something& other){
    other.value = 2;
}

int main(void){
    // valid usage
    something var1(18);
    // (do something with var1 and then re-use the variable)
    var1 = function1();

    // compile error
    something var2;
    function2(var2);
}

In case you are concerned about effiency, it should not matter whether you write your fill() to return a value, or to take output variable as a parameter. Your compiler should optimize it to the most efficient alternative of those two. If you suspect it doesn't, you had better measure it.

VLL
  • 9,634
  • 1
  • 29
  • 54