Подтвердить что ты не робот

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 и типами объектов, является ли это ошибкой или проблемой со стандартом?
  • Почему имеет значение, если что-то изменчиво?

Может кто-нибудь помочь мне обернуть мою голову вокруг этого, я действительно в затруднении здесь.

4b9b3361

Ответ 1

По-видимому, это способ устранения дефекта в стандарте, но вы не единственный, кто его смутил.

Из http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2094:

  1. Тривиальный конструктор копирования/перемещения для класса с летучим элементом

Раздел: 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):

  1. Является ли волатильный класс действительно 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 года:

Неясно, действительно ли изменчивый классификатор гарантирует атомарность таким образом. Кроме того, работа над моделью памяти для многопоточности, выполняемой Рабочей группой по эволюции, кажется, что в этот момент возможно указать дополнительную семантику для летучих данных, и эту работу необходимо будет рассмотреть до решения этой проблемы.