Использование std :: launder для получения указателя на активный член объединения из указателя на неактивный член объединения? - PullRequest
8 голосов
/ 25 мая 2019

Рассмотрим это объединение:

union A{
  int a;
  struct{
    int b;
    } c;
  };

c и a не совместимы с макетом типов, поэтому невозможно прочитать значение b через a:

A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)

По поводу пробной версии 1 и пробной версии 2 см. этот вопрос

Пробная версия 3

Теперь давайте используемstd::launder для чего он не предназначен:

A x;
x.a=10;
auto p = &x.a;                 //(1)
x.c.b=12;                      //(2)
p = std::launder(p);           //(2')
*p+*p;                         //(3)  UB?

Может ли std::launder что-то изменить?Согласно [ptr.launder] :

template <class T> constexpr T* launder(T* p) noexcept;

Требуется : p представляет адрес A байта в памяти.Объект X , срок действия которого совпадает с типом T, расположен по адресу A .Все байты памяти, которые будут доступны через результат, достижимы через p (см. Ниже).

Возвращает : значение типа T *, указывающее на X.

Замечания : вызов этой функции может использоваться в выражении основной константы всякий раз, когда значение ее аргумента может использоваться в выражении основной константы. Байт памяти доступен через значение указателя, которое указывает на объект Y , если он находится внутри хранилища, занятого Y, объекта, который является взаимозаменяемым с Y, или непосредственно в массиве, еслиY является элементом массива.Программа плохо сформирована, если T является типом функции или cv void.

Выделенное жирным шрифтом предложение подчеркивает то, что меня беспокоит.Если p является недопустимым значением указателя, как может быть доступен любой байт памяти?С другой стороны, при таком чтении std::launder просто невозможно использовать.

В противном случае значение может p в (2) быть значением указателя, которое представляет область хранения как естьо чем говорится в «Записке» в [basic.life] :

Если эти условия не выполнены, указатель на новый объект может быть получен изуказатель, представляющий адрес его хранилища путем вызова std​::​launder ([support.dynamic]).

1 Ответ

8 голосов
/ 26 мая 2019

В заметке допускается явно .

basic.life содержит следующее правило , которое делает std::launder ненужным:

Если по истечении времени жизни объекта и до повторного использования или освобождения хранилища, в котором занятый объект, создается новый объект в месте хранения, которое занимал исходный объект, указатель, указывающий на исходный объект,ссылка, которая ссылается на исходный объект, или имя исходного объекта будет автоматически ссылаться на новый объект и после запуска времени жизни нового объекта может использоваться для управления новым объектом, если:

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
  • новый объект того же типа, что и исходный объект (игнорируя квалификаторы cv верхнего уровня)и
  • тип исходного объекта не является константно-квалифицированным, и, если тип класса, не содержит никаких нестандартныхэлемент данных tic, тип которого является константным или ссылочным, и
  • ни исходный объект, ни новый объект не являются потенциально перекрывающимся подобъектом.

[Примечание: если этиусловия не выполняются, указатель на новый объект можно получить из указателя, который представляет адрес его хранилища, вызвав std::launder.- примечание конца]

Этот случай "новый объект создается в месте хранения, которое занимал исходный объект", явно применим здесь, потому что:

Объектсоздан ... при неявном изменении активного члена объединения ...

Все условия пули выполнены, поскольку «потенциально перекрывающийся подобъект» относится к базеподобъекты класса, членами которых не являются профсоюзы.(И в той версии, на которую вы ссылались, в этом пуле непосредственно упоминались подобъекты базового класса.)

Однако даже если бы эта интерпретация изменялась в отношении профсоюзов, в примечании конкретно упоминается, что std::launder обходит это ограничение.

Обратите внимание, что более старые версии Стандарта исключали подобъекты из этого правила ... но в примечании ясно, что std::launder обошло бы и эту проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...