Это строгое нарушение псевдонимов для псевдонима структуры в качестве первого члена? - PullRequest
0 голосов
/ 17 мая 2018

Пример кода:

struct S { int x; };

int func()
{
     S s{2};
     return (int &)s;    // Equivalent to *reinterpret_cast<int *>(&s)
}

Я считаю, что это распространено и считается приемлемым. Стандарт гарантирует, что в структуре нет начального заполнения. Однако этот случай не указан в правиле строгого алиасинга (C ++ 17 [basic.lval] / 11):

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

  • (11.1) динамический тип объекта,
  • (11.2) cv-квалифицированная версия динамического типа объекта,
  • (11.3) тип, аналогичный (как определено в 7.5) динамическому типу объекта,
  • (11.4) тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,
  • (11.5) тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа объекта,
  • (11.6) агрегатный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих элементов или нестатических элементов данных (включая, рекурсивно, элемент или нестатический элемент данных субагрегата или содержащего объединения),
  • (11.7) тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,
  • (11.8) тип char, unsigned char или std :: byte.

Кажется очевидным, что объект s имеет доступ к сохраненному значению.

Типы, перечисленные в пунктах маркированного списка, являются типом glvalue, осуществляющим доступ , а не типом объекта, к которому осуществляется доступ. В этом коде тип glvalue равен int, который не является агрегатным или объединенным типом, исключая 11.6.

У меня вопрос: правильный ли этот код, и если да, то под каким из вышеперечисленных пунктов он разрешен?

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Поведение приведения сводится к [expr.static.cast] / 13;

Значение типа «указатель на cv1 void» может бытьпреобразуется в значение типа «указатель на cv2 T», где T - это тип объекта, а cv2 - такая же квалификация cv, как и более высокая квалификация cvчем cv1 .Если исходное значение указателя представляет адрес A байта в памяти и A не удовлетворяет требованию выравнивания T, то результирующее значение указателя не определено. В противном случае, если исходное значение указателя указывает на объект a, и существует объект b типа T (игнорирующий квалификацию cv), который взаимозаменяем с указателем с помощью a, результатом является указатель на b. В противном случае значение указателя не изменяется при преобразовании.

Определение взаимозаменяемого указателя :

Два объекта a и b являются взаимозаменяемыми по указателю, если:

  • они являются одним и тем же объектом или
  • один является объектом объединения, а другой -является не статическим членом данных этого объекта, или
  • один является объектом класса стандартной компоновки, а другой является первым нестатическим членом данных этого объекта, или, если у объекта нетчлены статических данных, первый подобъект базового класса этого объекта или
  • существует объект c, такой, что a и c являются взаимно конвертируемыми по указателю, а c и b являются взаимно конвертируемыми по указателю.

Так в оригинальном коде s и s.x являются взаимозаменяемыми указателями , и из этого следует, что (int &)s фактически обозначает s.x.

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

0 голосов
/ 17 мая 2018

Я думаю, что это в expr.reinterpret.cast # 11

Выражение glvalue типа T1, обозначающее объект x, может быть приведено к типу «ссылка на T2» , если выражение типа «указатель на T1» может быть явно преобразовано в тип «указатель на T2» с использованием reinterpret_cast.В результате получается *reinterpret_­cast<T2 *>(p), где p - указатель на x типа «указатель на T1» .Временное создание не производится, копирование не производится, и конструкторы или функции преобразования не называются [1] .

[1] Это иногда называют введите каламбур когда результат ссылается на тот же объект, что и источник glvalue

Поддержка ответа @ MM о указатель-необратимый :

из 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;
...