Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is the C++ synthesized move constructor affected by volatile and virtual members?

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?

like image 201
user1827766 Avatar asked Nov 15 '12 22:11

user1827766


People also ask

Can a class have multiple move constructors?

there is no user-declared destructor . then the compiler will declare a move constructor as a non- explicit inline public member of its class with the signature T::T (T&&). A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&).

What is the purpose of the move constructor in C++?

The move constructor was introduced in C++11. The need or purpose of a move constructor is to steal or move as many resources as it can from the source (original) object, as fast as possible, because the source does not need to have a meaningful value anymore, and/or because it is going to be destroyed in a moment anyway.

What is trivial move constructor in C?

Trivial move constructor. A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.

What is volatile in C?

Introduction to Volatile in C. A volatile keyword in C is nothing but a qualifier that is used by the programmer when they declare a variable in source code. It is used to inform the compiler that the variable value can be changed any time without any task given by the source code. Volatile is usually applied to a variable when we are declaring it.


1 Answers

[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).

like image 72
Lightness Races in Orbit Avatar answered Nov 08 '22 10:11

Lightness Races in Orbit