Посмотрите на следующий код:
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?
[C++11: 12.8/9]:
Если определение классаX
явно не объявляет конструктор перемещения, он будет неявно объявлен как дефолт, если и только если[ Замечания: Когда конструктор перемещения не объявлен явно или не предоставлен явно, выражения, которые в противном случае
X
не имеет объявленного пользователем конструктора копирования,X
не имеет заявленного пользователем оператора копирования,X
не имеет объявленного пользователем оператора назначения перемещения,X
не имеет объявленного пользователем деструктора, а также- конструктор перемещения не будет неявно определен как удаленный.
вызвал бы конструктор перемещения, может вместо этого вызвать конструктор копирования. —Конечная записка]
Вот почему ваш № 3 нарушает синтез.
К тому же, это далеко не ясно тот изменчивые типы (в том числе ваш node* volatile
) тривиально копируемые; можно сделать вывод, что это определяется реализацией, являются ли они или нет и, в вашем случае, кажется, что это не так.
По крайней мере, GCC заставил его перестать работать вполне сознательно в v4.7, с предложением перенести обратно в v4.6.1, что я могу только предположить, пошел вперед …
Итак, учитывая следующее:
[C++11: 12.8/11]:
Неявно объявленный конструктор копирования / перемещения является встроенным открытым членом своего класса. По умолчанию конструктор копирования / перемещения для классаX
определяется как удаленный (8.4.3), еслиX
имеет:
- вариант члена с нетривиальным соответствующим конструктором и
X
это союзный класс,
нестатический член данных типа классаM
(или их массив), которые нельзя скопировать / переместить из-за разрешения перегрузки (13.3) применительно кM
Соответствующий конструктор, приводит к неоднозначности или функции, которая удалена или недоступна из конструктора по умолчанию,- прямой или виртуальный базовый класс
B
которые нельзя скопировать / переместить из-за разрешения перегрузки (13.3) применительно кB
Соответствующий конструктор, приводит к неоднозначности или функции, которая удалена или недоступна из конструктора по умолчанию,- любой прямой или виртуальный базовый класс или нестатический член данных типа с деструктором, который удален или недоступен из конструктора по умолчанию,
- для конструктора копирования — нестатический член данных ссылочного типа rvalue или
- для конструктора перемещения — нестатический элемент данных или прямой или виртуальный базовый класс с типом, который не имеет конструктора перемещения и не является легко копируемым.
… Вот почему ваш № 4 нарушает синтез, независимо от № 3.
Что касается # 5, это на самом деле не декларация node
вообще, но объявление для функции с именем m
— поэтому он не воспроизводит симптомы, связанные с node
(это известно как Most Vexing Parse).
Других решений пока нет …