Инициализаторы для объектов со статической продолжительностью хранения должны состоять из константных выражений. Как @EugenSh. в комментариях "постоянное выражение" - это определенный термин. В частности, в C2011 речь идет о разделе 6,6 . Описание просто
Постоянное выражение может быть оценено во время перевода, а не
время выполнения, и, соответственно, может использоваться в любом месте, где константа может
будет.
Но дьявол кроется в деталях. Семантические детали константных выражений содержат конкретные правила для определенных видов и использования константных выражений.
Например, выражение a
не является «целочисленным константным выражением» ни при каких обстоятельствах, независимо от типа или const
ness a
, и, следовательно, не может использоваться, если стандарт требует такого конкретного вида с постоянным выражением, например, в битовых полях.
Хотя стандарт не дает ему имя, он представляет несколько более смягченные правила для константных выражений в инициализаторах, что мы рассматриваем здесь:
Такое постоянное выражение должно быть одним из
следующее:
- выражение арифметической константы,
- константа нулевого указателя,
- адресная константа или
- адресная константа для полного типа объекта плюс или минус целочисленное константное выражение.
Определены также термины «выражение арифметической константы» и «постоянная адреса»:
Выражение арифметической константы должно иметь арифметический тип и должно
иметь только операнды, которые являются целочисленными константами, плавающими константами,
константы перечисления, символьные константы, размер выражений которых
Результатами являются целочисленные константы и выражения _Alignof
. [...]
Константа адреса - нулевой указатель, указатель на lvalue
обозначение объекта статической длительности хранения или указатель на
обозначение функции; он должен быть явно создан с использованием
оператор или целочисленная константа, приведенная к типу указателя, или неявно
использование выражения массива или типа функции. [...]
Ни один из инициализаторов для различных переменных b
не соответствует этим правилам. Выражение lvalue, обозначающее объект, имеющий const
-квалифицированный тип, не входит в число элементов, которым разрешено появляться в любой из разновидностей константного выражения, которые стандарт требует для инициализаторов.
Стандарт разрешает, как правило,
Реализация может принимать другие формы константных выражений.
, но это не отменяет его особых требований к константным выражениям, появляющимся в инициализаторах.
Каждое из приведенных объявлений переменной b
нарушает требование стандарта "must", появляющееся за пределами ограничения. Поэтому результирующее поведение не определено, но стандарт не требует диагностики. Реализации могут принимать такие формы в качестве расширения, как, очевидно, делает GCC 8.2, а опция GCC -pedantic
обеспечивает диагностику только в тех случаях, когда это требуется стандартом, который не включает эти случаи.
Поскольку поведение не определено, ни одно из наблюдаемых поведений различных реализаций не соответствует. Вы не можете полагаться на соответствующую реализацию для отклонения несоответствующего кода. При некоторых обстоятельствах (но не при этом) он должен диагностировать несоответствие, но даже в таких случаях ему разрешается успешно переводить в любом случае.
Я потерян. Ожидаемое поведение и такой код должен компилироваться?
Нет, но также небезопасно ожидать, что он не сможет скомпилироваться.
Почему / Почему нет?
Я объясняю выше, почему различные коды не соответствуют друг другу, и поэтому могут быть отклонены соответствующими компиляторами. Но с другой стороны, соответствующие компиляторы не обязаны отклонять несоответствующий код.
Что изменилось между gcc7.4 и gcc8.1? Это ошибка компилятора? Это расширение компилятора?
GCC, очевидно, реализовал расширение. Я не уверен, было ли это намеренно, но это, безусловно, результат. Пока поведение такое, чего можно наивно ожидать, оно выглядит довольно естественным и мягким, за исключением того, что GCC (не) помогает вам писать соответствующий код.