【问题标题】:How is the C++ synthesized move constructor affected by volatile and virtual members?volatile 和 virtual 成员如何影响 C++ 合成的 move 构造函数?
【发布时间】:2012-11-04 14:40:42
【问题描述】:

看下面的代码:

struct node
{

  node();
  //node(const node&);    //#1
  //node(node&&);         //#2

  virtual                 //#3
  ~node ();

  node*
  volatile                //#4
  next;

};

int main()
{

  node m(node());         //#5
  node n=node();          //#6
}

使用 gcc-4.6.1 编译时会产生以下错误:

g++ -g --std=c++0x   -c -o node.o node.cc
node.cc: In constructor node::node(node&&):
node.cc:3:8: error: expression node::next has side-effects
node.cc: In function int main():
node.cc:18:14: note: synthesized method node::node(node&&) first required here

据我了解,编译器无法在第 6 行创建默认移动或复制构造函数,如果我取消注释第 1 行或第 2 行,它编译得很好,这很清楚。代码在没有 c++0x 选项的情况下编译良好,因此错误与默认移动构造函数有关。

但是,节点类中的什么阻止了创建默认移动构造函数?如果我注释 #3 或 #4 中的任何一行(即使析构函数为非虚拟或使数据成员为非易失性)它会再次编译,那么这两者的组合是否使其无法编译?

另一个难题,第 5 行不会导致编译错误,与第 6 行有什么不同? 都是gcc特有的吗?还是 gcc-4.6.1?

【问题讨论】:

  • 你忘了main的返回类型有skippy

标签: c++ c++11 g++ move-constructor


【解决方案1】:

[C++11: 12.8/9]: 如果X类的定义没有明确声明移动构造函数,当且仅当

  • X 没有用户声明的复制构造函数,
  • X 没有用户声明的复制赋值运算符,
  • X 没有用户声明的移动赋值运算符,
  • X 没有用户声明的析构函数,并且
  • 移动构造函数不会被隐式定义为已删除。

[ 注意: 当移动构造函数没有被隐式声明或显式提供时,否则的表达式 将调用移动构造函数可能会调用复制构造函数。 ——尾注]

这就是你的 #3 打破综合的原因。

另外,it's far from clear那个volatile types (including your node* volatile) are trivially copyable;可以得出结论,it is implementation-defined whether they are or not,在你的情况下,它们似乎不是。

至少,在 v4.7 中,GCC made it stop working quite deliberately 提出了向后移植到 v4.6.1 的建议,我只能假设继续进行......

所以,给定以下内容:

[C++11: 12.8/11]: 隐式声明的复制/移动构造函数是其类的内联公共成员。 如果X 具有,则类X 的默认复制/移动构造函数被定义为已删除(8.4.3)

  • 具有非平凡对应构造函数的变体成员,X 是类联合类, 类类型M(或其数组)的非静态数据成员,因为重载决议(13.3)应用于M的相应构造函数,导致歧义或函数是从默认构造函数中删除或无法访问,
  • 无法复制/移动的直接或虚拟基类 B,因为应用于 B 的相应构造函数的重载决议 (13.3) 会导致歧义或函数从默认构造函数,
  • 任何直接或虚拟基类或具有析构函数的类型的非静态数据成员,该析构函数已从默认构造函数中删除或无法访问,
  • 对于复制构造函数,右值引用类型的非静态数据成员,或
  • 对于移动构造函数,非静态数据成员或直接或虚拟基类其类型不具有移动构造函数且不可轻易复制

...这就是为什么你的#4 也打破了综合,独立于#3。

至于 #5,这实际上根本不是 node 的声明,而是一个名为 m 的函数的声明——这就是为什么它没有重现与构造 node 相关的症状(这是称为Most Vexing Parse)。

【讨论】:

  • 谢谢,你说的很清楚。仍然让我感到困惑的是,这段代码运行良好:
  • 让我感到困惑的是,这段代码运行良好: struct node { ~node();节点* volatile next; };这也是: struct node { virtual ~node();节点*下一个; };所以 gcc-4.6.1 允许为 volatile 数据成员和带有析构函数的类移动 ctor,但不知何故抱怨虚拟 dtor/volatile 数据成员的组合。
  • 我想知道 volatile 可简单复制的问题是否只是某些实现想要赋予它的原子语义。如果是这样,在我看来,这种改变是不合适的,因为在 ISO C++ 中 volatile 没有原子语义
  • 我会,如果我有 4.7 :) 我需要保留 4.6.1,我不想同时有两个 gcc 版本。但这听起来不对,我同意。
猜你喜欢
  • 1970-01-01
  • 2020-11-21
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 2013-01-16
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
相关资源
最近更新 更多