简体   繁体   English

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

[英]Move-only type returned into converting constructor

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. 这给我的编译器带来了一个错误,只能通过将std::move添加到被认为是不良做法的return语句来修复,因为通常它会阻止返回值优化。 Shouldn't the identifier of a return statement be treated as rvalue first to satisfy conversions? 为了满足转换,首先应该将return语句的标识符视为rvalue吗?

Is this code valid and which compiler is right here? 这段代码是否有效,哪个编译器就在这里?

Shouldn't the identifier of a return statement be treated as rvalue first to satisfy conversions? 为了满足转换,首先应该将return语句的标识符视为rvalue吗?

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... ): 来自[class.copy] ,作为CWG 1579的结果(此处的措辞是从C ++ 17中复制的,虽然它在C ++ 14中是相同的。我发现子弹比早期的语法选择更容易阅读詹姆斯乔伊斯脸红......):

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 如果return语句中的表达式是一个(可能带括号的)id-expression,它指定一个对象,该对象具有在最内层封闭函数或lambda表达式的body或parameter-declaration-clause中声明的自动存储持续时间,或者
  • [...] [...]

overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. 首先执行重载决策以选择副本的构造函数,就好像该对象是由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. 如果第一个重载决策失败或未执行,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用 (可能是cv-qualified),则再次执行重载决策,将对象视为左值。

The first bullet applies here, so we first do overload resolution as if foo was an rvalue. 第一个子弹在这里适用,所以我们首先进行重载解析,好像foo是一个rvalue。 This gets is to the Other(Foo ) constructor by way of Foo(Foo&& ) . 这是通过Foo(Foo&& )获得的Other(Foo )构造函数。

The first parameter of Other(Foo ) is not an rvalue reference, so we should do overload resolution again considering the foo as an lvalue, which fails. Other(Foo )的第一个参数不是右值引用,所以我们应该再次进行重载分辨,将foo作为左值,它会失败。 This seems like an unnecessary restriction, but I'd call clang correct here. 这似乎是一个不必要的限制,但我在这里打电话给clang是正确的。 If you change the constructor to Other(Foo&& ) , clang accepts it. 如果将构造函数更改为Other(Foo&& ) ,则clang接受它。

Barry's fine answer covers the standard rules, but I have a practical suggestion: 巴里的答案很好地涵盖了标准规则,但我有一个实际的建议:

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. 并且只能通过将std::move添加到被认为是不良做法的return语句来修复,因为通常它会阻止返回值优化。

Your worry is unjustified. 你的担心是没有道理的。 NRVO does not apply to conversions anyway, and RVO on the result of the move is allowed. NRVO无论如何都不适用于转换,并允许RVO对移动结果。 The explicit move is fine. 明确的举动很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM