Является ли строгое нарушение псевдонимов псевдонимом структуры как своего первого члена?
Образец кода:
struct S { int x; };
int func()
{
S s{2};
return (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
}
Я считаю, что это распространено и считается приемлемым. Стандарт гарантирует, что в структуре нет начального заполнения. Однако этот случай не указан в правиле строгого псевдонимов (С++ 17 [basic.lval]/11):
Если программа пытается получить доступ к сохраненному значению объекта с помощью glvalue, отличного от одного из следующих типов, поведение не определено:
- (11.1) динамический тип объекта,
- (11.2) cv-квалификационная версия динамического типа объекта,
- (11.3) тип, аналогичный (как определено в 7.5) для динамического типа объекта,
- (11.4) тип, который является подписанным или неподписанным типом, соответствующим динамическому типу объекта,
- (11.5) тип, который является подписанным или неподписанным типом, соответствующим квитанционной версии динамического типа объекта,
- (11.6) совокупный или тип объединения, который включает один из вышеупомянутых типов среди его элементов или нестатических членов данных (включая рекурсивно элемент или нестатический элемент данных субагрегата или содержащегося объединения),
- (11.7) тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,
- (11.8) char, unsigned char или std :: byte type.
Кажется очевидным, что объект s
имеет доступ к сохраненному значению.
Типы, перечисленные в пунктах пули, являются типом glvalue, выполняющим доступ, а не типом объекта, к которому осуществляется доступ. В этом коде тип glvalue является int
который не является агрегатным или объединенным типом, исключая 11.6.
Мой вопрос: правильно ли этот код, и если да, то в каком из перечисленных пунктов можно разрешить?
Ответы
Ответ 1
Поведение броска сводится к [expr.static.cast]/13;
PRvalue типа "указатель на cv1 void
" может быть преобразован в prvalue типа "указатель на cv2 T
", где T
- тип объекта, а cv2 - это то же самое cv-qualification, что и более высокая cv-квалификация, чем cv1. Если исходное значение указателя представляет адрес A
байта в памяти, и A
не удовлетворяет требованию выравнивания T
, то результирующее значение указателя не указывается. В противном случае, если исходное значение указателя указывает на объект a
, и есть объект b
типа T
(игнорирующий cv-qualification), который является взаимно конвертируемым с a
, результатом является указатель на b
. В противном случае значение указателя не изменяется при преобразовании.
Определение взаимно-обратимого указателя:
Два объекта a и b являются взаимопереключаемыми с указателем, если:
- они являются одним и тем же объектом или
- один представляет собой объект объединения, а другой - нестатический элемент данных этого объекта или
- один является объектом класса стандартного макета, а второй является первым нестатическим членом данных этого объекта или, если у объекта нет нестатических элементов данных, первый подобъект базового класса этого объекта или
- существует объект c такой, что a и c являются взаимно обратимыми для указателей, а c и b являются взаимно обратимыми.
Таким образом, в исходном коде s
и sx
являются взаимно sx
для указателей, и из этого следует, что (int &)s
самом деле обозначает sx
.
Таким образом, в правиле строгого сглаживания объект, чье хранимое значение обращается, является sx
а не s
и поэтому нет проблем, код верен.
Ответ 2
Я думаю, что это в expr.reinterpret.cast # 11
Выражение glval типа T1, обозначающее объект x
, может быть передано типу "ссылка на T2", если выражение типа "указатель на T1" может быть явно преобразовано в тип "указатель на T2" с использованием reinterpret_cast. В результате получается *reinterpret_cast<T2 *>(p)
где p
является указателем на x
типа "указатель на T1". Временное создание не производится, копирование не производится, а конструкторы или функции преобразования не называются [1].
[1] Это иногда называют калом типа, когда результат ссылается на тот же объект, что и исходный glvalue
Поддержка ответа @MM об указателе-incovertible:
из cppreference:
Предполагая, что требования к выравниванию выполнены, reinterpret_cast
не изменяет значение указателя за пределами нескольких ограниченных случаев, связанных с объектами, конвертируемыми с указателем:
struct S { int a; } s;
int* p = reinterpret_cast<int*>(&s); // value of p is "pointer to s.a" because s.a
// and s are pointer-interconvertible
*p = 2; // s.a is also 2
против
struct S { int a; };
S s{2};
int i = (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
// i doesn't change S.a;