How is the C++ synthesized move constructor affect

2019-03-25 16:01发布

Look at the following code:

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
}

When compiled with gcc-4.6.1 it produces the following error:

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

As I understand the compiler fails to create default move or copy constructor on line #6, if I uncomment either line #1 or #2 it compiles fine, that is clear. The code compiles fine without c++0x option, so the error is related to default move constructor.

However, what in the node class prevents default move constructor to be created? If I comment any of the lines #3 or #4 (i.e. make the destructor non-virtual or make data member non-volatile) it compiles again, so is it the combination of these two makes it not to compile?

Another puzzle, line #5 does not cause an compilation error, what is different from line #6? Is it all specific for gcc? or gcc-4.6.1?

1条回答
别忘想泡老子
2楼-- · 2019-03-25 16:20

[C++11: 12.8/9]: If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared move assignment operator,
  • X does not have a user-declared destructor, and
  • the move constructor would not be implicitly defined as deleted.

[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. —end note ]

That's why your #3 is breaking the synthesis.

In addition, it's far from clear that volatile types (including your node* volatile) are trivially copyable; it could be concluded that it is implementation-defined whether they are or not and, in your case, it seems that they are not.

At the very least, GCC made it stop working quite deliberately in v4.7, with a proposal to backport into v4.6.1 that I can only presume went ahead...

So, given the following:

[C++11: 12.8/11]: An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:

  • a variant member with a non-trivial corresponding constructor and X is a union-like class, a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
  • a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
  • any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
  • for the copy constructor, a non-static data member of rvalue reference type, or
  • for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.

... that's why your #4 is breaking the synthesis too, independently of #3.

As for #5, that's not actually a declaration of a node at all, but a declaration for a function called m — that's why it's not reproducing the symptoms related to construction of a node (this is known as the Most Vexing Parse).

查看更多
登录 后发表回答