【问题标题】:Will member subobjects of local variables be moved too if returned from a function?如果从函数返回,局部变量的成员子对象也会被移动吗?
【发布时间】:2012-02-07 20:06:03
【问题描述】:

C++11 标准规定,如果满足复制省略的条件 (§12.8/31),则实现应将 returned 局部左值变量和函数参数视为右值优先(移动),如果重载解析没有成功,则应将其视为左值(副本)。

§12.8 [class.copy] p32

当满足或将满足删除复制操作的条件时,除了源对象是函数参数这一事实之外,并且要复制的对象由左值指定,重载决议选择复制的构造函数首先执行,就好像对象是由右值指定的一样。如果重载决议失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。 [ 注意: 无论是否会发生复制省略,都必须执行此两阶段重载解决方案。它确定如果不执行省略则要调用的构造函数,并且即使调用被省略,所选构造函数也必须是可访问的。 ——尾注 ]

这是否也包括成员子对象?我用下面的 sn-p 进行了测试:

#include <iostream>

struct traced{
  traced(){ std::cout << "default ctor\n"; }
  traced(traced const&){ std::cout << "copy ctor\n"; }
  traced(traced&&){ std::cout << "move ctor\n"; }
};

struct X{
  traced t;
};

traced f(){
  X x;
  return x.t;
}

int main(){
  traced t = f();
}

Live example on Ideone. 而且 GCC 4.7 ToT 和 Clang 3.1 ToT 都不会显示“move ctor”,这让我相信标准不包括成员子对象。

我是否忽略了什么?我的测试代码坏了吗?究竟是什么导致输出保持原样?

【问题讨论】:

  • 我不认为你的测试证明他们不能。它可能只是表明编译器很难确认 criteria for elision 已经满足,因为突出显示的部分只有在编译器可以确认标准有效时才相关。
  • 我有点困惑。我想我混合了返回值优化和复制省略。我稍微调整了代码并得到了移动,请参阅this demo。我直接从f() 返回 x,允许 RVO。它使用 traced t = f().t; 来演示这一举动。 (我不知道这是否有帮助!)
  • @Aaron。 RVO 只是复制省略的一种应用。 :) 一个适当的子集,可以这么说。此外,返回后访问的好主意。可悲的是,这不适用于我的实际代码。 ://

标签: c++ c++11 local-variables move-semantics


【解决方案1】:

当返回一个子对象时,你不能省略它的构造。可以这样想:移动和复制省略本质上相当于在最终将被移动或复制到的位置构建对象。这适用于完整的对象,因为将留出适当的空间。它不适用于子对象,因为您将构造封闭对象。即使它与子对象具有相同的大小,即有足够的空间,封闭对象也会被破坏并且可能对子对象做一些有趣的事情。

实际上,这意味着不能省略主语的构造。

【讨论】:

  • 这意味着§12.8/32的整个段落将不适用。我想知道为什么标准不简单地说,如果返回的对象是本地左值或按值 func 参数,则应该移动它,否则应该复制它。在这里只依赖复制省略的资格似乎有点严格。有什么原因吗?
  • @Richard:不错。 :) 如果没有特殊的理由禁止这样做,也许您可​​以在会议上提出我评论中的最后一点? :)
  • Richard 现在与他在核心工作组的同事坐在一起可能会更好地回答这个问题(不过,我是图书馆的人,今天坐在 Evolution 中)。我的猜测是这些词可以有效地将一些规则委托给现有的规范:这些东西通常是经过仔细措辞的,以避免可能相当微妙的不一致。此外,你的话并不完全适用:据我所知,这些词的想法是找出是否可以移动或复制类型,因为这是可能被省略的先决条件。
  • 嗯,但据我了解,return 语句中局部变量的移动是根据复制省略指定的...
  • 虽然您所说的关于 NRVO 的内容可能是正确的,但对于复制到移动的处理方式却不一定正确。移动子对象而不是复制它没有技术问题。像return X().t; 这样的代码已经要求只移动一个子对象而不是整个完整的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-11
  • 1970-01-01
  • 2019-09-03
  • 2020-06-22
  • 1970-01-01
相关资源
最近更新 更多