Подтвердить что ты не робот

Почему не сужение конверсии, используемой с инициализатором с зависящими от фигурной скобки, вызывает ошибку?

Я узнал о инициализаторе с зависящими от фигурной скобки в языке программирования С++, 4-е изд. > Глава 2: Обзор С++: Основы.

Я цитирую приведенную ниже книгу.

Форма = традиционная и относится к C, но если есть сомнения, используйте общую форму {} -list (§6.3.5.2). Если ничего больше, это избавит вас от конверсий, которые теряют информацию (сужение конверсий, §10.5):

int i1 = 7.2;    // i1 becomes 7
int i2 {7.2};    // error : floating-point to integer conversion
int i3 = {7.2};  // error : floating-point to integer conversion (the = is redundant)

Однако я не могу воспроизвести эти результаты.

У меня есть следующий код.

#include <iostream>

int main()
{
    int i1 = 7.2;
    int i2 {7.2};
    int i3 = {7.2};

    std::cout << i1 << "\n";
    std::cout << i2 << "\n";
    std::cout << i3 << "\n";
}

Когда я компилирую и запускаю его, я не получаю никаких ошибок. Я получаю предупреждение о std=c++11, но без ошибок.

$ g++ init.cpp 
init.cpp: In function ‘int main()’:
init.cpp:6:12: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
     int i2 {7.2};
            ^
$ ./a.out 
7
7
7

Кроме того, предупреждение предназначено только для второго задания, но для третьего назначения нет предупреждения. Это, по-видимому, указывает на то, что = не является излишним, как указано в книге. Если = были избыточными, либо второе, либо третье присваивания выдавали предупреждения или оба не выдавали бы предупреждения. Затем я скомпилирую их с флагом -std=c++11.

$ g++ -std=c++11 init.cpp 
init.cpp: In function ‘int main()’:
init.cpp:6:16: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
     int i2 {7.2};
                ^
init.cpp:7:18: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
     int i3 = {7.2};
                  ^
$ ./a.out 
7
7
7

По-прежнему нет ошибок. Только предупреждения. Хотя в этом случае второе и третье присваивания ведут себя одинаково в отношении генерирования предупреждений.

Итак, мой вопрос: хотя в книге упоминается, что второе и третье присваивания являются ошибками, почему этот код не скомпилирован?

4b9b3361

Ответ 1

Это плохо сформировано и должно быть диагностическим, однако оно может быть либо предупреждением (которое вы получили), либо ошибкой. gcc сделал это предупреждение для нескольких версий из-за проблемы переноса с С++ 03:

Стандарт требует только, чтобы "соответствующая реализация выдавала хотя бы одно диагностическое сообщение", поэтому разрешается компиляция программы с предупреждением. Как сказал Эндрю, -Werror = сужение позволяет вам сделать ошибку, если вы хотите.

g++ 4.6 дала ошибку, но была изменена на предупреждение намеренно на 4.7, потому что многие люди (включая меня) обнаружили, что сужение конверсий, когда одна из наиболее часто встречающихся проблем при попытке скомпилировать большие С++ 03 кодовые базы как C + +11. Ранее сформированный код, такой как char c [] = {i, 0}; (где я будет только когда-либо находиться в диапазоне char), вызвал ошибки и должен был быть изменен на char c [] = {(char) i, 0}

но теперь последние версии gcc и clang делают эту ошибку, видеть ее вживую для gcc.

Для справки черновик сценария С++ 11 8.5.4 [dcl.init.list] говорит:

В противном случае, если в списке инициализаторов имеется один элемент, объект или ссылка инициализируется из этого элемента; если сужение конверсии (см. ниже) требуется преобразовать элемент в T, программа плохо сформирован. [Пример:

int x1 {2}; // OK
int x2 {2.0}; // error: narrowing

-end пример]

и

Сужение преобразования является неявным преобразованием

  • от типа с плавающей точкой до целочисленного типа или

[...]

[Примечание. Как указано выше, такие преобразования не допускаются на верхнем уровне в списке-инициализации.-end примечание] [Пример:

[...]

int ii = {2.0}; // error: narrows

[...]

Таким образом, преобразование с плавающей точкой в ​​целое число является преобразованием сужения и плохо сформировано.

и раздел 1.4 Выполнение соответствия [intro.compliance] говорит:

Хотя в этом международном стандарте указаны только требования к реализациям на С++, эти требования часто легче понять, если они сформулированы как требования к программам, частям программ или выполнение программ. Такие требования имеют следующее значение:

[...]

  • Если программа содержит нарушение любого диагностируемого правила или появление конструкции, описанной в этот стандарт "условно поддерживается", когда реализация не поддерживает эту конструкцию, соответствующая реализация должна выпустить хотя бы одно диагностическое сообщение.

[...]

Сообщает нам, что требуется только диагностика.

Ответ 2

Язык С++ не отличает "предупреждения" от "ошибок". С++ содержит только диагностические сообщения. Предупреждения, которые вы получили, являются диагностическими сообщениями. Спецификация языка не требует от компиляторов прекращения компиляции, когда они сталкиваются с ошибочным (ака неправильно сформированным) кодом. Все компиляторы должны сделать, это выдать диагностическое сообщение, а затем они могут продолжить компиляцию, если они того пожелают.

Это означает, что в общем случае ваша ответственность - сообщать предупреждения, которые являются "просто предупреждениями", из предупреждений, которые на самом деле указывают на подлинные ошибки, особенно с такими разрешительными компиляторами, как GCC.

Это также означает, что фактическое поведение в реальной жизни является вопросом настройки вашего компилятора. Попросите вашего компилятора быть более ограничительным в этом отношении, если это возможно. В GCC вы можете попробовать -pedantic-errors для этой цели.

P.S. В моих экспериментах с GCC -std=c++11 достаточно, чтобы заставить его генерировать ошибки для вашего кода. Если вы получаете предупреждения, это может быть вопросом компилятора.