The following code returns a move-only type that should then be converted to another type by a converting constructor.
#include <utility>
class Foo
{
public:
Foo() {}
Foo(const Foo&) = delete;
Foo(Foo&&) = default;
};
class Other
{
public:
Other(Foo foo) {}
};
Other moo()
{
Foo foo;
return foo;
}
int main()
{
moo();
}
This threw me an error with my compiler and could only be fixed by adding std::move
to the return statement which is considered bad practice, because in general it prevents return value optimization. Shouldn't the identifier of a return statement be treated as rvalue first to satisfy conversions?
Is this code valid and which compiler is right here?
- g++, c++14: compiles: http://coliru.stacked-crooked.com/a/f25ae94e8ca9c5c8
- g++-4.8, c++11: does not compile: http://coliru.stacked-crooked.com/a/0402e3ebf97fd0e7
- clang++, c++14: does not compile: http://coliru.stacked-crooked.com/a/682d8ca93d3e2f6a
Yes and no. From [class.copy], as a result of CWG 1579 (the wording here is copied from C++17, although it's the same in C++14. I find the bullets easier to read than the earlier grammar choice that would make James Joyce blush... ):
The first bullet applies here, so we first do overload resolution as if
foo
was an rvalue. This gets is to theOther(Foo )
constructor by way ofFoo(Foo&& )
.The first parameter of
Other(Foo )
is not an rvalue reference, so we should do overload resolution again considering thefoo
as an lvalue, which fails. This seems like an unnecessary restriction, but I'd call clang correct here. If you change the constructor toOther(Foo&& )
, clang accepts it.