Стандарт сообщает нам 1 в примере 2 , что допустимо следующее:
struct T1 { int a, b; };
struct T2 { int c; double d; };
union U { T1 t1; T2 t2; };
int f() {
U u = { { 1, 2 } }; // active member is t1
return u.t2.c; // OK, as if u.t1.a were nominated
}
Теперь добавляем уровень косвенности (функция-член) иинтересно, удаляя другое (объединение):
Является ли следующее хорошо определенным?
struct T1 { int a; int value() { return a; }};
struct T2 : T1 { int b; };
int f() {
T1 t1 = { 1 };
return reinterpret_cast<T2*>(&t1)->value();
}
Я знаю, что приведение не является неопределенным поведением 3 само по себе;но оператор ->
в его результате нарушает [basic.lval]/11
4 ?или мы спасены [expr.ref]/4
5 ?
Я хочу думать, что это законно, поскольку t1.a
и reinterpret_cast<T2*>(&t1)->a
имеют тот же адрес 6 (отсюда и то же смещение в их представлении).Но так ли это?
1) [class.mem]/22
и [class.mem]/23
определяют, какие совместимые с макетом классы есть;[class.mem]/25
дает нам:
В объединении стандартных макетов с активным членом типа структуры T1
разрешено читатьнестатический член данных m другого члена объединения типа структуры T2
, при условии m
является частью общей начальной последовательности из T1
и T2
;поведение такое, как если бы был назначен соответствующий член T1
.
2) In [class.mem]/25
.
3) [expr.reinterpret.cast]/7
:
Указатель объекта может быть явно преобразован в указатель объекта другого типа.Когда значение v типа указателя объекта преобразуется в тип указателя объекта «указатель на cv T
», результатом будет static_cast<cv T*>(static_cast<cv void*>(v))
.
и [expr.static.cast]/13
Значение типа «указатель на cv1 void
» может быть преобразовано в значение типа «указатель на cv2 T
», гдеT
- это тип объекта, а cv2 - это та же квалификация cv, что и квалификация cv или более высокая, чем cv1 .Если исходное значение указателя представляет адрес A
байта в памяти и A
не удовлетворяет требованию выравнивания T
, то результирующее значение указателя не определено.В противном случае, если исходное значение указателя указывает на объект a
, и существует объект b
типа T
(игнорирующий квалификацию cv), который взаимозаменяем с указателем a
, результатом является указатель наb
.В противном случае значение указателя при преобразовании не изменяется.
4) [basic.lval]/11
Если программа пытаетсячтобы получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено:
- [правила не применимы]
- (11.6) илитип объединения, который включает один из вышеупомянутых типов среди своих элементов или нестатических элементов данных (включая, рекурсивно, элемент или элемент нестатических данных субагрегированного или автономного объединения),
- [правила не применимы]
5) [expr.ref]/4
в постфиксном выражении, включающем нотацию E1.E2
или E1->E2
.
6) [class.mem]/26
Если у стандартного макета класса есть какие-либо элементы не статических данных, его адресом являетсятакой же, как адрес его первого нестатического элемента данных, если этот элемент не является битовым полем.Его адрес также совпадает с адресом каждого из его подобъектов базового класса.