Std:: is_trivially_copyable - Почему неустойчивые скалярные типы не тривиально скопируются?
Существующие стандарты для С++ 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 и типами объектов, является ли это ошибкой или проблемой со стандартом?
- Почему имеет значение, если что-то изменчиво?
Может кто-нибудь помочь мне обернуть мою голову вокруг этого, я действительно в затруднении здесь.
Ответы
Ответ 1
По-видимому, это способ устранения дефекта в стандарте, но вы не единственный, кто его смутил.
Из http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2094:
- Тривиальный конструктор копирования/перемещения для класса с летучим элементом
Раздел: 12.8 [class.copy] Статус: открыт Отправитель: Daveed Vandevoorde Дата: 2015-03-06
Решение вопроса 496 включено добавление пункта 25.2 в 12.8 [class.copy], создание класса copy/move конструктор нетривиальный, если он имеет нестатический элемент данных нестабильного типа. Это изменение нарушает ABI IA-64, поэтому было предложено, чтобы CWG пересмотрела этот аспект резолюции.
В соответствующем примечании, решение вопроса 496 также изменилось 3.9 [basic.types], пункт 9, который дает нестабильные скалярные типы "тривиальным", но не "тривиально скопируемым". Неясно, почему существует сделанное здесь; единственное фактическое использование "тривиального типа" в Стандарт, как представляется, содержится в описании qsort, который должен вероятно, использовать "тривиально с возможностью копирования". (См. Также выпуск 1746.)
Из описания вопроса (от 30.12.2004):
- Является ли волатильный класс действительно POD?
Однако в пункте 3.9 [basic.types], стандарт дает понять что POD можно скопировать "как бы", они были набором байтов на тетср:
Для любого типа POD T, если два указателя на T указывают на различные T-объекты obj1 и obj2, где ни obj1, ни obj2 не являются подобъектом базового класса, если значение obj1 копируется в obj2, используя std:: memcpy библиотечная функция, obj2 впоследствии будет иметь то же значение, что и obj1. Проблема заключается в том, что может оказаться, что нестабильный квалифицированный тип скопированы определенным образом (путем копирования с использованием только атомных операций на многопоточные платформы, например), чтобы избежать "памяти" разрыв ", который может произойти с байтовой побайтовой копией.
Я понимаю, что в стандарте очень мало говорится о неустойчивых квалифицированных типы и вообще ничего (о) о многопоточных платформах, но тем не менее это реальная проблема по следующей причине:
Предстоящий TR1 определит ряд признаков, которые предоставляют информацию о свойствах типа, включая тип POD и/или имеет тривиальные операции построения/копирования/назначения. Библиотеки могут использовать эту информацию для оптимизации своего кода, например, массив типа T может быть скопирован с memcpy, а не по отдельности, если T является POD. Это было одной из основных мотивов, лежащих в основе главы свойств типов TR1. Однако неясно, как в этих случаях следует обращаться с изменчивыми типами (или POD, которые имеют изменчивый тип в качестве члена). Примечания из собрания в апреле 2005 года:
Неясно, действительно ли изменчивый классификатор гарантирует атомарность таким образом. Кроме того, работа над моделью памяти для многопоточности, выполняемой Рабочей группой по эволюции, кажется, что в этот момент возможно указать дополнительную семантику для летучих данных, и эту работу необходимо будет рассмотреть до решения этой проблемы.