Существующие стандарты для С++ 17 (и я наблюдал аналогичную формулировку для С++ 11), имеют очень запутанную формулировку для типов, которые можно копировать тривиально. Я впервые наткнулся на эту проблему со следующим кодом (GCC 5.3.0):
class TrivialClass {};
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
Усугубляя путаницу, я попытался проверить, что std::is_trivial
должен был сказать по этому поводу, только будучи более путаным.
class TrivialClass {};
std::is_trivial<int volatile>::value; // 1 ??
std::is_trivial<TrivialClass volatile>::value; // 1
Confused, я проверил последний черновик С++ 17, чтобы увидеть, что-то не так, и я нашел немного неоднозначную формулировку, которая может быть виновником:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73
cv-неквалифицированные скалярные типы, тривиально-скопируемые типы классов (раздел 9), массивы таких типов и нелетучие версии с независимыми от констант этих типов (3.9.3) совместно называются тривиально-скопируемыми типами.
Ниже приведена информация о тривиально сшиваемых классах:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.226
Тривиально-скопируемый класс - это класс, который:
- (6.1) не имеет нетривиальных конструкторов копирования (12.8),
- (6.2) не имеет нетривиальных конструкторов перемещения (12.8),
- (6.3) не имеет нетривиальных операторов присваивания копий (13.5.3, 12.8),
- (6.4) не имеет нетривиальных операторов присваивания перемещения (13.5.3, 12.8) и
- (6.5) имеет тривиальный деструктор (12.4).
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#section.12.8
Конструкторы:
Конструктор копирования/перемещения для класса X тривиален, если он не предоставляется пользователю, его список параметров-параметров эквивалентен списку параметров-типа неявного объявления, а если
- (12.1) класс X не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
- (12.2) класс X имеет нестатические элементы данных с нестабильным типом и
- (12.3) конструктор, выбранный для копирования/перемещения каждого подобъекта прямого базового класса, тривиален и
- (12.4) для каждого нестатического элемента данных X, относящегося к типу класса (или его массиву), конструктор, выбранный для копирования/перемещения этого элемента, тривиален;
в противном случае конструктор copy/move не является тривиальным.
Назначение:
Оператор присваивания копирования/перемещения для класса X является тривиальным, если он не предоставляется пользователем, его список параметров-параметров эквивалентен списку параметров-типа неявного объявления, а если
- (25.1) класс X не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
- (25.2) класс X имеет нестатические элементы данных с нестабильным типом и
- (25.3) оператор присваивания, выбранный для копирования/перемещения каждого подобъекта прямого базового класса, тривиален, а
- (25.4) для каждого нестатического элемента данных X, который имеет тип класса (или его массив), оператор присваивания, выбранный для копирования/перемещения этого элемента, тривиален;
иначе оператор присваивания копирования/перемещения нетривиален.
Примечание. Обновлен этот раздел с дополнительной информацией. Теперь я считаю, что это ошибка в GCC. Однако это одно не отвечает на все мои вопросы.
Я мог видеть, что, возможно, это потому, что у TrivialClass нет нестатических членов, поскольку это передало бы вышеприведенные правила, поэтому я добавил int, и он по-прежнему возвращается как тривиально скопируемый.
class TrivialClass { int foo; };
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
В стандарте указано, что volatile должен быть унаследован под-объектами изменчивого объекта. Значение TrivialClass volatile
нестатический член данных foo
должен теперь иметь тип int volatile
.
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.76
Волатильный объект является объектом типа volatile T, подобъектом такого объекта или изменчивым подобъектом константного volatile объекта
Мы можем подтвердить, что это работает в GCC через:
std::is_same<decltype(((TrivialClass volatile*)nullptr)->foo), int volatile>::value; // 1! (Expected)
Смущенный, я затем добавил volatile к int foo
. Он по-прежнему проходит, что, очевидно, является ошибкой!
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68905#c1
class TrivialClass { int volatile foo; };
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
Двигаясь дальше, мы видим, что std::is_trivial
также работает так, как ожидалось:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73
Скалярные типы, тривиальные типы классов (раздел 9), массивы таких типов и cv-квалификационные версии этих типов (3.9.3) коллективно называются тривиальными типами.
Хорошо, поэтому у меня здесь много вопросов.
- Почему летучее вещество для is_trivially_copyable и не is_trivial?
- Какая сделка с is_trivially_copyable и типами объектов, является ли это ошибкой или проблемой со стандартом?
- Почему имеет значение, если что-то изменчиво?
Может кто-нибудь помочь мне обернуть мою голову вокруг этого, я действительно в затруднении здесь.