Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't these variables be moved?

There seems to be a lot of missed opportunity in the C++ move semantics. I'd like to understand the rationale behind those and why the standard isn't more aggressive at defining when a variable should be moved in the following cases:

string f()
{
    string s;
    return s + " ";
}

This calls operator+(const string&, const char*), not operator+(string&&, const char*), I believe because s is an lvalue. Couldn't the standard say that, in the last use of a local variable in a function, the variable is to be considered movable?

I think a somewhat similar example:

struct A { A(string&&); };
string g()
{
    string s;
    return s; // s is moved
}
A h()
{
    string s;
    return s; // s can't be moved!
}

g uses move semantics to move the data from s to the return value, but h doesn't compile because s is not moved in h. I believe this is because the standard has a special case for g where it says essentially that if you return a local variable of the exact same type as the return type, the variable is moved. Why isn't the rule that if you return a local variable, it's moved, regardless of its type?

like image 820
anonymous Avatar asked Nov 29 '12 16:11

anonymous


Video Answer


2 Answers

I'm sure it could require a move either example, but then someone would come up with another case where they think it's "obvious" that s is being used for the last time, and therefore should be moved.

Ultimately you'd have the standard defining what data-flow analysis the compiler is required to perform. The authors decided to draw the line conservatively, to allow implementations to be stupid in that respect. Programmers can always write std::move to change a copy to a move.

Another possibility would be for the standard to say that it is unspecified whether objects are moved or copied, provided that the code doesn't use them again. That would allow the implementation to be as clever as it likes. I'm pretty sure that would be a bad idea: in practice users often don't care whether their objects are moved, but they sometimes need to work it out.

like image 158
Steve Jessop Avatar answered Sep 16 '22 14:09

Steve Jessop


Simply put, it's an unnecessary restriction in the current standard, which makes automatic moves depend on the availability of copy-elision.

Another example would be:

struct X{
  std::string s;
};

std::string foo(){
  X x;
  return x.s; // no automatic move
};

I opened a thread on http://isocpp.org's Future Standard Proposals forum, which can be seen here. On the advice of Richard Smith, I directly mailed to Mike Miller about opening a core issue on this and got this response:

[...] based on Richard's summary above, it does sound like a reasonable question, so I'll open an issue for it in the next revision of the issues list. Thanks.

So for C++14, all these restrictions will likely go away and everytime a local variable is returned, you'll get an automatic move.

Richard's summary btw is this:

More concretely, [class.copy]p31 has a rule that the copy can be elided for a statement "return id-expression;" where the id-expression names a local variable, and the variable has the same cv-unqualified type as the function's return type. The suggestion is that we should perform an automatic move any time we have a statement "return id-expression;" where the id-expression names a local variable or function parameter, regardless of the type of the variable.

like image 44
Xeo Avatar answered Sep 17 '22 14:09

Xeo