Как синтезированный конструктор перемещения С++ влияет на изменчивых и виртуальных членов?

Посмотрите на следующий код:

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, она компилируется отлично, это ясно. Код компилируется без опции С++ 0x, поэтому ошибка связана с конструктором перемещения по умолчанию.

Однако, что в классе node предотвращает создание конструктора перемещения по умолчанию? Если я прокомментирую какую-либо из строк # 3 или # 4 (т.е. Сделайте деструктор не виртуальным или сделайте элемент данных нелетучим), он снова скомпилируется, так же как и комбинация этих двух делает невозможным компиляцию?

Еще одна головоломка, строка # 5 не вызывает ошибку компиляции, что отличает ее от строки # 6? Это все специфично для gcc? или gcc-4.6.1?

Ответ 1

[C++11: 12.8/9]: Если определение класса X явно не объявляет конструктор перемещения, оно будет объявлено как неявное как дефолтное, если и только если

  • X не имеет объявленного пользователем конструктора копирования,
  • X не имеет объявленного пользователем оператора назначения копирования,
  • X не имеет объявленного пользователем оператора назначения перемещения,
  • X не имеет объявленного пользователем деструктора и
  • конструктор перемещения не будет неявно определен как удаленный.

[Примечание. Если конструктор перемещения неявно не объявлен или явно не указан, выражения, которые в противном случае мог бы вызвать конструктор перемещения, может вместо этого вызвать конструктор копирования. -end note]

Вот почему ваш № 3 нарушает синтез.

Кроме того, это далеко не ясно, что летучие типы (включая ваш node* volatile) тривиально копируются; можно сделать вывод, что определяется реализацией, являются они или нет, и в вашем случае кажется, что это не так.

По крайней мере, GCC заставил его перестать работать совершенно преднамеренно в версии 4.7, с предложением включить в v4.6.1 Я могу только предположить, пошел вперед...

Итак, учитывая следующее:

[C++11: 12.8/11]: Неявно объявленный конструктор copy/move является встроенным публичным членом своего класса. Конструктор копирования/перемещения по умолчанию для класса X определяется как удаленный (8.4.3), если X имеет:

  • вариантный член с нетривиальным соответствующим конструктором и X является объединенным классом, нестатический элемент данных типа класса M (или его массив), которые невозможно скопировать/перемещать, поскольку разрешение перегрузки (13.3), применимое к соответствующему конструктору M, приводит к неоднозначности или функции, которая удалена или недоступен из конструктора, установленного по умолчанию,
  • прямой или виртуальный базовый класс B, который не может быть скопирован/перемещен, поскольку разрешение перегрузки (13.3) применительно к соответствующему конструктору B приводит к двусмысленности или функции, которая удалена или недоступна по умолчанию конструктор,
  • любой прямой или виртуальный базовый класс или нестатический член данных типа с деструктором, который удален или недоступен из конструктора, установленного по умолчанию,
  • для конструктора копирования, нестатического элемента данных ссылочного типа rvalue или
  • для конструктора перемещения, нестатического элемента данных или прямого или виртуального базового класса с типом, который не имеет конструктора перемещения и не является тривиально скопируемым.

... почему ваш # 4 также нарушает синтез, независимо от № 3.

Что касается № 5, это вообще не декларация node, а объявление для функции с именем M — почему он не воспроизводит симптомы, связанные со строительством node (это называется Most Vexing Parse).