Инициализация const int литералом с плавающей запятой

Пример

int main()
{
    const int i = 1.0; // Notice I am assigning a double to an int here
    char a[i];
}

Вопрос

Компиляция приведенного выше кода с g++ -O0 -Wall -pedantic -ansi -std=c++11 не дает ошибок (за исключением неиспользуемой переменной). Однако, если я удалю -std=c++11, я получу следующее предупреждение:

предупреждение: ISO C++ запрещает массив переменной длины

Согласно этому вопросу SO, я считаю, что в C++03 , код недействителен. Однако может ли кто-нибудь объяснить, как правило изменилось в С++ 11?

(Этот вопрос был результатом предыдущий вопрос я ответил.)


person Jesse Good    schedule 07.06.2012    source источник
comment
Вы хотели сделать i двойным, а не целым?   -  person David    schedule 07.06.2012
comment
@Dave: Нет, это было специально. Смотрите ссылку на предыдущий вопрос.   -  person Jesse Good    schedule 07.06.2012
comment
FWIW, clang++ 3.0 не показывает предупреждение VLA. У меня нет g++, чтобы проверить это, но я полагаю, что предупреждение не исчезнет, ​​если вы измените назначение на const int i = 1;. Имеет ли это?   -  person jweyrich    schedule 07.06.2012
comment
@jweyrich: компиляция с -pedantic в clang++ 3.1 дает аналогичное предупреждение. Если вы измените его на const int i = 1;, предупреждение исчезнет.   -  person Jesse Good    schedule 07.06.2012


Ответы (1)


Связанный массив должен быть интегральным константным выражением, см. 8.3.4 [dcl.array]/1 (та же формулировка в C++03 и C++11):

Если константное выражение (5.19) присутствует, оно должно быть интегральным постоянным выражением, и его значение должно быть больше нуля.

В C++03 целочисленное константное выражение не может быть инициализировано литералом с плавающей запятой, если только оно не приведено к целочисленному типу, см. последнее предложение в 5.19 [expr.const]/1:

Интегральное выражение-константа может включать только литералы (2.13), перечислители, const переменные или статические элементы данных целочисленного или перечисляемого типов, инициализированные константными выражениями (8.5), параметры нетипового шаблона интегрального или перечисляемого типы и sizeof выражения. Плавающие литералы (2.13.3) могут появляться только в том случае, если они приведены к целочисленному типу или типу перечисления.

Это означает, что в C++03 i не является целочисленным константным выражением, поэтому его нельзя использовать в качестве связанного массива.

GCC и Clang допускают массивы переменной длины в качестве расширения C++03, поэтому он компилируется с неконстантной привязкой, но вы получаете предупреждение с -pedantic. Изменение инициализатора константы для приведения ее к целочисленному типу делает i допустимым целочисленным константным выражением:

const int i = (int) 1.0;

С этим изменением массив больше не имеет переменной длины, и нет предупреждения даже с -pedantic.

В С++ 11 5.19 [expr.const]/3 говорится:

Литеральное константное выражение – это основное константное выражение prvalue литерального типа, но не типа указателя. Интегральное константное выражение – это буквальное константное выражение интегрального типа или перечисления с незаданной областью.

Предыдущие (довольно длинные) абзацы описывают правила для основных константных выражений, но в основном в C++11 двойной инициализатор не препятствует тому, чтобы i было основным константным выражением, даже без приведения, поэтому это целочисленное константное выражение и, следовательно, действительная привязка массива, поэтому никаких предупреждений.

person Jonathan Wakely    schedule 07.06.2012
comment
GCC не предупреждает о неправильной инициализации целочисленной константы. Он предупреждает об использовании расширения компилятора VLA (которое C++11 больше не использует, так как в C++11 массив можно инициализировать константой). - person Konrad Rudolph; 07.06.2012
comment
@Konrad, да, только что отредактировал, чтобы уточнить, что я имел в виду под «разрешить». - person Jonathan Wakely; 07.06.2012