10

看下面的代码:

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?

4

1 回答 1

12

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

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

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

这就是为什么你的#3 打破了综合。

此外,还不清楚volatile 类型(包括 your node* volatile)是否可以简单地复制;可以得出结论,无论它们是否是实现定义的,在您的情况下,它们似乎不是。

至少,GCC在 v4.7 中故意让它停止工作,并提议将其向后移植到 v4.6.1,我只能假设继续进行......

因此,鉴于以下情况:

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

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

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

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

于 2012-11-15T22:35:33.137 回答