Вызов метода через плохо напечатанный указатель?Это законно? - PullRequest
0 голосов
/ 30 ноября 2018

Стандарт сообщает нам 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

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

1 Ответ

0 голосов
/ 30 ноября 2018

Нет, насколько я могу судить, это UB.

Я не понимаю, как [expr.ref] p4 может вас спасти.Вы получаете доступ к объекту типа T1 через указатель другого типа (который также не является динамическим типом объекта).

Кроме того, [class.mem] p26 не имеет значения, поскольку выуже нарушает [basic.lval] p11.

...