0
votes

Today I found that this code doesn't work as I expect it to work. According to my knowledg for L-values copy constructor should be called while for R-values it should choose move constructor. Otherwise what's the purpose of std::move which actually does nothing but casts to R-value. I was expecting that return obj will call copy constructor, but it calls move. I understand that copy is useless here, but that's about rules. What if my copy constructor has a side effect and that's my case (I know that it shouldn't, but technically it can - for example here: std::cout call). Is there anything in standard that allows such behavior? Also how can I force a copy?

#include <iostream>

class X
{
public:
    X() = default;

    X(const X& r): i(r.i)
    {
        std::cout << "copy ctor" << std::endl;
    }

    X(const X&& r): i(r.i)
    {
        std::cout << "move ctor" << std::endl;
    }
    int i = 0;
};

X foo()
{
    X obj;
    obj.i = 10;
    return obj;
}

int main()
{
    X x = foo();
}

move ctor

move ctor

1

1 Answers

2
votes

From cppreference (emphasis mine):

If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const). The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)

Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).