Рассмотрим этот код:
#include <iostream>
int main()
{
int i{10.1}; // narrowing, should not compile
std::cout << i << std::endl;
}
Согласно стандарту C ++ 11, он не должен компилироваться (сужение в фигурной скобке инициализации запрещено.)
Теперь, компилируя с g++4.9.2 -std=c++11
только выдает предупреждение
warning: narrowing conversion of '1.01e+1' from 'double' to 'int' inside { } [-Wnarrowing]
Удаление -std=c++11
Флаг приводит к предупреждению относительно скобки init, но не к сужению:
warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
С другой стороны, g ++ 5 не компилирует это, если вы компилируете с g++5 -std=c++11
, Однако если -std=c++11
опущено, то даже g++5
счастливо компилирует это, давая только предупреждение, связанное с инициализацией скобки, а не сужением:
warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
Вышеуказанное поведение кажется ошибочным, g++4.9
не должен компилировать код, и это более чем странно, что g++5
компилирует его, если вы забудете указать -std=c++11
, Это известная проблема?
Стандарт никогда ничего не говорит «не должен компилироваться» (кроме . Некоторые плохо сформированные программы должны испускать диагностику и выдача предупреждения удовлетворяет это.#error
)
Вы можете заставить gcc остановить сборку во всех диагностиках, используя переключатель -Werror
, Его можно сузить до конкретных предупреждений, например, -Werror=narrowing
,
Если вы компилируете в GNU ++ или в любом другом режиме по умолчанию вместо C ++ 11, то компилятор может делать все что угодно, в том числе принимать сужающие преобразования без жалоб.
Ссылка: N3936 [intro.compliance] / 2
[Defns.diagnostic]
Если программа содержит нарушение какого-либо диагностируемого правила […], соответствующая реализация должна выпустить хотя бы одно диагностическое сообщение.
Если программа содержит нарушение правила, для которого не требуется диагностика, настоящий международный стандарт не устанавливает требований в отношении реализации этой программы.
диагностическое сообщение
сообщение, принадлежащее определенной подмножеству выходных сообщений реализации
Обратите также внимание на то, что из первого пункта маркировки не требуется, чтобы количество или содержание сообщений соответствовало количеству или содержанию нарушений.
Стандарт оставляет за собой полностью право компилятора решать, как организовать свои ошибки и / или предупреждения, при условии, что при определенных нарушениях он не может игнорировать его.
Причина в том, что сужение конверсии внутри {}
Только ошибка в режиме C ++ 11 проста: это не ошибка в C ++ 03. Сейчас, T var{value};
новый синтаксис C ++ 11, но T var = {value};
был уже действительный синтаксис C ++ 03, и сделал разрешить сужение конверсий.
int i = { 10.1 }; // valid C++03, invalid C++11
Это облегчает разработчикам GCC одинаковую обработку сужающихся конверсий в T var{value};
а также T var={value};
инициализацый. Это полезно, поскольку позволяет избежать двух отдельных путей кода для предупреждения в компиляторе.
Разработчикам GCC легче принять даже T var{value};
синтаксис в режиме C ++ 03, просто предупреждение об этом. Несколько других синтаксических расширений C ++ 11 также включены в режиме C ++ 03. Это полезно, потому что несколько расширений синтаксиса C ++ 11 используются в реализации стандартной библиотеки GCC (где предупреждения об этом подавляются).
Причина того, что int i{10.1};
не ошибка в GCC 4.9 в режиме C ++ 11, но была допущена ошибка в GCC 5 из-за того, что из-за неправильного обращения с ней допустимый код был отклонен. Стандарт C ++ требует трактовать его как ошибку в контекстах SFINAE, и вот действительная программа C ++ 11, которая из-за этого некорректно работает с GCC 4.9:
#include <stdio.h>
template <typename T> void f(double) { puts("ok"); }
template <typename T, typename = decltype(T{10.1})> void f(int) { puts("error"); }
int main() { f<int>(1); }
Это должно напечатать «хорошо». Вторая перегрузка должна быть отброшена.
В GCC 4.9 выводится «ошибка», поскольку вторая перегрузка не сбрасывается, и int
это лучший матч, чем double
,
Цитировать из 1.4 [intro.compliance]
Соответствующая реализация может иметь расширения (включая дополнительные
библиотечные функции), при условии, что они не изменяют поведение любого
правильно сформированная программа. Реализации необходимы для диагностики программ
которые используют такие расширения, которые плохо сформированы в соответствии с этим
Международный стандарт. Сделав это, однако, они могут скомпилировать и
выполнять такие программы.
Применимый раздел для вашего примера инициализации 8.5.4 [dcl.init.list]
, Особенно,
В противном случае, если список инициализатора имеет единственный элемент типа Е а также
или T не является ссылочным типом или его ссылочный тип
ссылка связана с Е , объект или ссылка инициализируется из
этот элемент; если требуется сужающее преобразование (см. ниже)
преобразовать элемент в T , программа плохо формируется.
сопровождается примером
int x1 {2}; // OK
int x2 {2.0}; // error: narrowing
Поскольку точный характер диагностики определяется реализацией, оба наблюдаемых набора поведения соответствуют стандарту.