union' punning 'structs w / " common initial sequence": почему C (99+), но не C++, предусматривает "видимое объявление типа объединения"?

фон

обсуждение в основном не-или-реализации-определенного характера типа-каламбура через union обычно цитируют следующие биты, здесь через @ecatmur (https://stackoverflow.com/a/31557852/2757035), об исключении для типовой формы structs, имеющие "общую начальную последовательность" типов членов:

C11 (6.5.2.3 структура и члены профсоюза; семантика):

[...] если объединение содержит несколько структур, имеющих общую начальную последовательность (см. ниже), и если объект объединения в настоящее время содержит одну из этих структур, позволено проверить общая начальная часть любого из них везде, где декларация завершенный тип объединения виден. Две структуры разделяют общая начальная последовательность если соответствующие члены имеют совместимые типы (и, для битовых полей, одинаковые ширины) для последовательности одного или больше первоначальных членов.

C++03 ([класс.mem] / 16):

если POD-union содержит две или более POD-структур, которые разделяют общую начальную последовательность, и если POD-union объект в настоящее время содержит один из этих POD-структур разрешается проверять общий начальный часть любого из них. Две подструктуры имеют общую начальную последовательность если соответствующие члены имеют совместимость с макетом типы (и, для битовые поля одинаковой ширины) для последовательности из одного или нескольких начальных члены.

другие версии двух стандартов языка; начиная с C++11 используемая терминология стандартные-макета, а не POD.

поскольку никакая переинтерпретация не требуется, это на самом деле не каламбур, просто замена имени применяется к union доступ к членам. Предложение для C++17 (печально P0137R1) делает это явным, используя язык, такой как "доступ, как будто другой член структуры был назначен".

но, пожалуйста, обратите внимание на жирный ... --15-->везде, где отображается объявление завершенного типа объединения" - предложение, которое существует в C11, но нигде в черновиках C++ для 2003, 2011 или 2014 (все почти идентичные, но более поздние версии заменяют " POD " новым термином стандартный формат). В любом случае, видимое объявление union тип бит полностью отсутствует в соответствующем разделе любого стандарта C++.

@loop и @Mints97, здесь -https://stackoverflow.com/a/28528989/2757035 - показать, что эта строка также отсутствует в C89, впервые появляется в C99 и оставаясь в C с тех пор (хотя, опять же, никогда не фильтруется до C++).

стандарты обсуждения вокруг этого

[отрезал-см. Мой ответ]

вопросы

от этого, тогда, мои вопросы были:--12-->

  • что это значит? что классифицируется как "видимое объявление"? Было ли это положение призвано сузить - или расширить-диапазон контекстов, в которых такая "каламбурность" определила поведение?

  • мы должны предположить, что это упущение в C++ очень преднамеренно?

  • в чем причина отличия C++ от C? сделал C++ просто "унаследовать" это от C89, а затем либо решить - или хуже,забыть - обновить вместе с C99?

  • если разница намеренная, то Какие преимущества или недостатки существуют для 2 различных методов лечения в C vs C++?

  • Какие, если есть, интересные последствия он имеет при компиляции или во время выполнения? например, @ecatmur, в комментарии, отвечая на мое указание на его оригинал ответ (ссылка выше), рассуждали следующим образом.

Я бы предположил, что это позволяет более агрессивную оптимизацию; C может предположить, что аргументы функции S* s и T* t не псевдоним, даже если они разделяют общая начальная последовательность до тех пор, пока нет union { S; T; } находится в поле зрения, хотя C++ может сделать это предположение только во время ссылки. Может быть стоит задавая отдельный вопрос об этом различии.

Ну, вот я, спрашиваю! Я очень интересуют любые мысли об этом, особенно: другие соответствующие части (либо) стандарта, цитаты из членов комитета или других уважаемых комментаторов, идеи от разработчиков, которые могли бы заметить практическую разницу из - за этого-предполагая, что любой компилятор даже мешает для обеспечения выполнения добавленного предложения C - и т. д. Цель состоит в том, чтобы создать полезный каталог соответствующих фактов об этом предложении C и его (намеренном или нет) пропуске из C++. Итак, поехали!

2 ответов


я нашел свой путь через лабиринт к некоторым отличным источникам об этом, и я думаю, что у меня есть довольно полное резюме этого. Я публикую это как ответ, потому что это, похоже, объясняет как (ИМО очень ошибочное) намерение предложения C, так и тот факт, что C++ не наследует его. Это будет развиваться со временем, если я обнаружу дополнительные вспомогательные материалы или ситуация изменится.

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

Наконец, некоторые конкретные комментарии

через неопределенно связанные темы я нашел следующий ответ @tab - и высоко оценил содержащиеся ссылки на (освещающие, если не окончательные) отчеты о дефектах GCC и рабочей группы:ответ по вкладке на StackOverflow

в Ссылка GCC содержит некоторые интересные обсуждения и показывает значительное количество путаницы и противоречивых интерпретаций со стороны комитета и поставщиков компиляторов - вокруг темы union s, каламбур и сглаживание в C и c++.

в конце концов, мы связаны с основным событием-другой поток BugZilla,65892 ошибка С очень полезное обсуждение. В частности, мы находим наш путь к первому из два ключевых документа:--27-->

начало добавленной строки в C99

C предложение N685 происхождение добавлен пункт касательно видимость union объявление типа. Благодаря тому, что некоторое утверждение (см. поток GCC #2) является полной неправильной интерпретацией "общей начальной последовательности", n685 действительно предназначен для релаксации правил сглаживания для "общей начальной последовательности"structs в пределах TU известно о некоторых union содержащие экземпляры said struct типы, как мы видим из этой цитаты:

предложенное решение требует, чтобы быть союзом декларации видно если псевдонимы через общую начальную последовательность (как указано выше) возможны. Поэтому следующий TU обеспечивает такой вид сглаживания при желании:

union utag {
    struct tag1 { int m1; double d2; } st1;
    struct tag2 { int m1; char c2; } st2;
};

int similar_func(struct tag1 *pst2, struct tag2 *pst3) {
     pst2->m1 = 2;
     pst3->m1 = 0;   /* might be an alias for pst2->m1 */
     return pst2->m1;
}

судя по обсуждению GCC и комментариям ниже, таким как @ecatmur, это предложение-которое, похоже, санкционирует спекулятивно разрешение сглаживания для любого struct тип, который, например, в некоторых union видимый для этого TU -кажется, получил большую насмешку и редко был реализован.

очевидно, как трудно было бы удовлетворить эту интерпретацию добавленного предложения, не полностью калеча многие оптимизации-для небольшой выгоды, так как мало кодеров хотели бы эту гарантию, а те, кто это делает, могут просто включить fno-strict-aliasing (что ИМО указывает на большие проблемы). Если реализованная, эта надбавка, скорее всего, выловит людей и злонамеренно взаимодействует с другими декларациями unions, чем быть полезным.

пропуск строки из C++

следуя из этого и комментария, который я сделал в другом месте,@Potatoswatter в этом ответе здесь на SO гласит:

часть видимости была намеренно опущена из C++, потому что она широко считается смехотворной и нереализуемые.

другими словами, похоже на С++ сознательно избегал принятия этого добавили пункт, скорее всего из-за его широко pereceived абсурда. попросив" на запись " цитату из этого, Potatoswatter предоставил следующую ключевую информацию об участниках потока:

люди в этой дискуссии по существу" на записи " там. Эндрю Пински-хардкорный бэкэнд GCC. Мартин Себор является активным C член комитета. Джонатан Уэйкли-активный член комитета C++ и разработчик языка/библиотеки. Эта страница более авторитетна, ясна и полна, чем все, что я мог бы написать.

Potatoswatter, в том же потоке SO, связанном выше, делает вывод, что C++ намеренно исключил эту строку, не оставив специального лечения (или, в лучшем случае, определенного реализацией лечения) для указателей в общую начальную последовательность. Будет ли их лечение в будущем конкретно определенные, по сравнению с любыми другими указателями, еще предстоит увидеть; сравните с моим заключительным разделом ниже О C. В настоящее время, хотя это не так (и снова, ИМО, это хорошо).

что это означает для C++ и практических реализаций C?

Итак, с гнусной линией от N685... ' cast в сторону'... мы вернулись к предположению, что указатели на общую начальную последовательность не являются особыми с точки зрения сглаживания. До сих пор. стоит подтвердить, что этот абзац на C++ значит, без него. Ну, 2-й поток GCC выше ссылки на другой драгоценный камень:

дефект C++ 1719. Это предложение достигло DRWP статус: "проблема DR, решение которой отражено в текущем рабочем документе. Рабочий документ представляет собой проект будущего варианта стандарта" -цитировать. Это либо сообщение C++14, либо, по крайней мере, после окончательного проекта, который у меня есть здесь (N3797) - и выдвигает значительно, и в моем мнение освещающее, перепишите формулировку этого абзаца, следующим образом. Я выделяю то, что считаю важными изменениями, и {эти замечания} мое:

в стандартной разметки союз с активным {"активный" означает union экземпляр, а не только тип} (9.5 [класс.союз]) структуры типа T1 разрешается читать {ранее "проверить"} a нестатический элемент данных m другого члена Союза структуры типа T2 предоставил m часть общая начальная последовательность T1 и T2. [Примечание: чтение Летучего объекта через энергонезависимый glvalue имеет неопределенное поведение (7.1.6.1 [dcl.типа.cv]). - конец Примечания]

это, кажется, проясняет смысл старой формулировки: для меня это говорит о том, что любое специально разрешено 'каламбурно между union член structs с общими начальными последовательностями должно быть сделано через экземпляр родитель union - вместо того, чтобы основываться на типе structs (например, указатели на них передаются некоторые функции). Эта формулировка, по-видимому, исключает любую другую интерпретацию,а-ля N685. C было бы неплохо принять это, я бы сказал. Эй, кстати, см. ниже!

в результате этого-как хорошо продемонстрировано @ecatmur и в билетах GCC-это листья такие union s по определению в C++ и практически в C, с учетом тех же строгих правил псевдонима, что и любые другие 2 официально несвязанных указателя. явное гарантия умея читать общую начальную последовательность неактивных union s теперь более четко определен, не включая расплывчатую и невообразимо утомительную для обеспечения "видимости" как попытка по N685 для C. По этому определению основными компиляторами были поведение, предназначенное для C++. Что касается C?

возможный разворот этой строки в C / clarification в C++

также очень стоит отметить, что член комитета C Мартин Себор тоже хочет исправить это на этом прекрасном языке:

Мартин Себор 2015-04-27 14:57: 16 UTC если один из вас может объяснить проблему с ним, я готов написать статью и представить ее WG14 и попросить изменить стандарт.

Мартин Себор 2015-05-13 16: 02: 41 UTC у меня была возможность обсудить этот вопрос с Кларком Нельсоном на прошлой неделе. Кларк работал над улучшением частей сглаживания спецификации C в прошлом, например, в N1520 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm). Он согласился с тем, что, как и проблемы, отмеченные в N1520, это также нерешенная проблема, которую WG14 стоило бы пересмотреть и устанавливать."

Potatoswatter вдохновенно заключает:

комитеты C и C++ (через Мартина и Кларка) попытаются найти консенсус и выработать формулировку, чтобы стандарт мог, наконец, сказать, что это значит.

мы можем только надеяться!

опять же, все дальнейшие мысли приветствуются.


Я подозреваю, что это означает, что доступ к этим общим частям разрешен не только через тип союза, но и вне союза. То есть, предположим, мы имеем следующее:

union u {
  struct s1 m1;
  struct s2 m2;
};

теперь предположим, что в некоторой функции у нас есть struct s1 *p1 указатель, который мы знаем, был снят с m1 - члены такого союза. Мы можем бросить это struct s2 * указатель и по-прежнему доступ к членам, которые являются общими с struct s1. Но где-то в области, декларация union u должен быть видимый. И это должно быть полное объявление, которое сообщает компилятору, что члены struct s1 и struct s2.

вероятным намерением является то, что если есть такой тип в области, то компилятор знает, что struct s1 и struct s2 псевдонимы, и поэтому доступ через struct s1 * указатель подозревается в действительно доступе к struct s2 или наоборот.

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

поскольку формулировка отсутствует в C++, то, чтобы воспользоваться правилом "общей релаксации начальных членов" на этом языке, вы должны маршрутизировать доступ через тип объединения, как это обычно делается в любом случае:

union u *ptr_any;
// ...
ptr_any->m1.common_initial_member = 42;
fun(ptr_any->m2.common_initial_member);  // pass 42 to fun