返回到转换构造函数中的仅移动类型

Move-only type returned into converting constructor

以下代码 return 是一个只能移动的类型,然后应该通过转换构造函数将其转换为另一种类型。

#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();
}

这给我的编译器带来了一个错误,只能通过将 std::move 添加到 return 语句来修复,这被认为是不好的做法,因为通常它会阻止 return 值优化。 return 语句的标识符是否应该首先被视为右值以满足转换?

这段代码有效吗?哪个编译器就在这里?

Shouldn't the identifier of a return statement be treated as rvalue first to satisfy conversions?

是也不是。来自 [class.copy], as a result of CWG 1579 (the wording here is copied from C++17, although it's the same in C++14。我发现项目符号比早期的语法选择更容易阅读,这会让 James Joyce 脸红...):

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

  • If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or
  • [...]

overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.

第一条适用于此,因此我们首先进行重载解析,就好像 foo 是右值一样。这通过 Foo(Foo&& ).

获取到 Other(Foo ) 构造函数

Other(Foo ) 的第一个参数不是右值引用,因此我们应该将 foo 视为左值,再次进行重载解析,但失败了。这似乎是一个不必要的限制,但我认为这里的 clang 是正确的。如果将构造函数更改为 Other(Foo&& ),clang 会接受它。

Barry 的回答很好,涵盖了标准规则,但我有一个实用的建议:

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.

你的担心是多余的。 NRVO 无论如何都不适用于转换,并且允许对移动结果进行 RVO。显式移动没问题。