Question:
When returning a local object by value, C++ compilers may optimize out unnecessary copies (copy elision) by taking advantage of the move semantics.
"may optimize" implies that if proper conditions are not met, the behavior should fall back to the default return by value semantics, based on copy.
Thus, as I understand it, it is always valid to return a copyable object by value.
But compilers (clang and gcc) do not seem to agree with my interpretation, as shown by the MWE below.
class Foo {
public:
Foo();
Foo(const Foo&);
Foo(Foo&&) = delete;
}
Foo f() { return Foo(); } // error: call to explicitly deleted constructor of 'Foo'
Foo g() { Foo a; return a; } // gcc complains, clang is fine
Foo x = g(); // error: call to explicitly deleted constructor of 'A'
Q1: Does return by value requires object to be movable?
Q2: If not, do gcc and clang misbehave on my MWE or am I missing something else?
Answer:
You are simply meeting the intended behaviour of overload resolution: Foo()
is an rvalue, so overload resolution finds the constructor Foo(Foo&&)
as the best match. Since that overload is deleted, your program is ill-formed. Moreover, there's a special rule that says Foo a; return a;
will also perform overload resolution as if a
was an rvalue first. (The rule applies essentially whenever the the return statement is eligible for copy elision.)
This is all working as intended. It was you who deleted the overload, so you requested expressly that such constructions be forbidden.
Note that "real" code doesn't usually meet this obstacle, since as soon as you declare a copy constructor, your class will not have any move constructors at all. But you went out of your way to say, "no, actually I do want a move constructor, and I want it to be an error if anyone attempts to use it".