【问题标题】:Move-only type returned into converting constructor只移动类型返回到转换构造函数
【发布时间】:2018-02-16 06:44:23
【问题描述】:

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

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

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

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

【问题讨论】:

  • IIRC、C++11 和 C++14 在这一点上有所不同。
  • @Jarod42 在那种情况下,我希望两个编译器都同意 C++14 模式。
  • 编译器不必对此达成一致。可悲的是,这种复制省略只是他们可能做的事情。不是他们必须做的事。允许 Clang 完全无视它可以用副本代替移动的事实。
  • @user2079303 - 这是非常可选的。 [class.copy] 描述了整个事情的地方,并没有强制实现省略任何内容。它仅指定实现何时允许的先决条件。
  • @user2079303 - 你知道的。我们在这件事上白费力气。 OP 没有返回与函数返回值具有相同(可能是 cv 限定)类型的对象。不符合 p31 中指定的省略标准。所以这没有实际意义。

标签: c++ language-lawyer move-semantics rvalue


【解决方案1】:

return语句的标识符不应该先被当作右值来满足转换吗?

是和不是。来自[class.copy],作为CWG 1579 的结果(这里的措辞是从C++17 复制的,尽管在C++14 中是一样的。我发现项目符号比早期的语法选择更容易阅读,这会使詹姆斯乔伊斯脸红……):

在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:

  • 如果return 语句中的表达式是一个(可能是带括号的)id 表达式,它命名一个对象,该对象具有在最内层封闭函数或 lambda 表达式的主体或参数声明子句中声明的自动存储持续时间,或者
  • [...]

首先执行为副本选择构造函数的重载决策,就好像对象是由右值指定的一样。如果第一个重载决议失败或未执行,或如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则执行重载决议再次,将对象视为左值。

第一个项目符号在这里适用,所以我们首先进行重载解析,好像foo 是一个右值。这是通过Foo(Foo&amp;&amp; ) 传递给Other(Foo ) 构造函数的。

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

【讨论】:

  • 我放弃了。我不适合在此处对 ATM 的措辞做出正面或反面。 +1(尽管您引用的是 C++17,而不是 C++14)。
  • @StoryTeller 与C++14 中的措辞相同,只是更难阅读。
  • 几乎不一样,否则我不会这么生气。我的观点是,帮助破译 C++14 中的措辞可能比提供明确说明的 C++17 引用更为相关。
  • @StoryTeller 是一样的。 “当 return 语句中的表达式是一个(可能带括号的)id 表达式时,它命名一个具有在主体中声明的自动存储持续时间的对象 [...]由右值指定。如果第一个重载决议失败或未执行,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。”
  • @typ1232 是一个clang错误,至少在3.9.1修复了。
【解决方案2】:

巴里的好答案涵盖了标准规则,但我有一个实用的建议:

并且只能通过在 return 语句中添加 std::move 来修复,这被认为是不好的做法,因为通常它会阻止返回值优化。

你的担心是没有道理的。 NRVO 无论如何都不适用于转换,并且允许对移动结果进行 RVO。明确的举动很好。

【讨论】:

    猜你喜欢
    • 2012-07-12
    • 1970-01-01
    • 2011-05-22
    • 2015-07-08
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多