Рассмотрим этот фрагмент кода C ++ 11:
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
При инициализации происходит сужающееся преобразование из двойного в бул. x
, Согласно моему пониманию стандарта, это плохо сформированный код, и мы должны увидеть некоторую диагностику.
Visual C ++ 2013 выдает ошибку:
error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
Тем не менее, оба Clang 3.5.0 и GCC 4.9.1, используя следующие параметры
-Wall -Wextra -std=c++11 -pedantic
скомпилировать этот код с без ошибок и предупреждений. Запуск программы выводит 1
(нет ничего удивительного).
Теперь давайте углубимся в странную территорию.
+ Изменить X(bool arg)
в X(int arg)
и внезапно мы получили ошибку от Clang
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
и предупреждение от GCC
warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]
Это больше похоже на то, что я ожидал.
Теперь держите bool
аргумент конструктора (то есть вернуться к X(bool arg)
) и изменить double d = 7.0;
в int d = 7;
, Опять же, сужающаяся ошибка от Clang, но GCC вообще не выдает никакой диагностики и компилирует код.
Есть еще несколько вариантов поведения, которые мы можем получить, если передадим константу непосредственно конструктору, некоторые странные, некоторые ожидаемые, но я не буду перечислять их здесь — этот вопрос становится слишком длинным.
Я бы сказал, что это один из редких случаев, когда VC ++ верен, а Clang и GCC ошибочны, когда дело доходит до соответствия стандартам, но, учитывая соответствующие достижения этих компиляторов, я все еще очень сомневаюсь в этом.
Что думают эксперты?
Стандартные ссылки (цитаты из окончательного стандартного документа для C ++ 11, ISO / IEC 14882-2011):
В параграфе 3 8.5.4 [dcl.init.list] мы имеем:
— В противном случае, если T является типом класса, учитываются конструкторы. Применимые конструкторы перечислены
и лучший выбирается через разрешение перегрузки (13.3, 13.3.1.7). Если сужение конверсии (см.
ниже) требуется преобразовать любой из аргументов, программа некорректно сформирована.
В том же разделе, в пункте 7, мы имеем:
Сужающее преобразование — это неявное преобразование
— от типа с плавающей точкой к целочисленному типу, или
— от длинных двойных к двойным или плавающим, или от двойных к плавающим, кроме случаев, когда источник является константой
выражение и фактическое значение после преобразования находится в диапазоне значений, которые могут быть представлены
(даже если это не может быть представлено точно), или
— от целочисленного типа или типа перечисления с незаданной областью до типа с плавающей запятой, кроме случаев, когда источник
является постоянным выражением, и фактическое значение после преобразования будет соответствовать целевому типу и будет
создать исходное значение при преобразовании обратно в исходный тип или
— от целочисленного типа или типа перечисления с незаданной областью до целочисленного типа, который не может представлять все
значения исходного типа, кроме случаев, когда источником является константное выражение, а фактическое значение после
преобразование будет соответствовать целевому типу и даст исходное значение при преобразовании обратно в
оригинальный тип.
[Примечание: как указано выше, такие преобразования не допускаются на верхнем уровне при инициализации списка. — конец
нота ]
В пункте 7 3.9.1 [basic.fundamental] мы имеем:
Типы bool, char, char16_t, char32_t, wchar_t, а также целочисленные типы со знаком и без знака являются общими
называемые целочисленными типами.48 Синонимом целочисленного типа является целочисленный тип.
(Я начинал подвергать сомнению все на этом этапе …)
Это просто выглядит как ошибка, если мы попробуем следующее:
bool b {3} ;
и то и другое gcc
а также clang
выдать диагностику, например gcc
говорит:
предупреждение: сужение преобразования «3» из «int» в «bool» внутри {} [-Wararrowing]
Это покрыто в проект стандарта C ++ 11 по разделу 8.5.4
Список инициализация параграф 7 который говорит:
Сужающее преобразование — это неявное преобразование
[…]
- от целочисленного типа или перечислимого типа с незаданной областью к целочисленному типу
которые не могут представлять все значения исходного типа, кроме
где источник является константным выражением, а фактическое значение после
преобразование будет соответствовать целевому типу и будет производить оригинал
значение при преобразовании обратно в исходный тип.
Это тот же абзац, который охватывает ваш пример и следующий более простой пример:
bool a {3.0} ;
который будет охватываться этой пулей из пункта 7
цитируется выше:
- от типа с плавающей точкой до целочисленного типа, или
Из пункта 3
Это плохо сформировано и требует диагностики:
Инициализация списка объекта или ссылки типа T определяется следующим образом:
[…]
- В противном случае, если список инициализаторов имеет единственный элемент, объект или ссылка инициализируются из этого
элемент; если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа
плохо сформирован.
который gcc
не производит никакой диагностики, но clang
выдает следующее предупреждение, хотя мы не увидим предупреждение о сужающемся преобразовании:
предупреждение: неявное преобразование из ‘double’ в ‘bool’ меняет значение с 3 на true [-Wliteral-преобразование]
Примечание, раздел 3.9.1
[Basic.fundamental] говорит:
Типы BOOL, char, char16_t, char32_t, wchar_t, а также целочисленные типы со знаком и без знака являются общими
называется интегральные типы.48 Синоним для целочисленного типа — целочисленный тип..
Вы должны подать отчет об ошибке с обоими лязг а также НКУ.
Джонатан Уэйкли отмечает, что компилятор EDG выдает ошибку сужения для кода OP, что является убедительным свидетельством того, что это действительно должно привести к диагностике.
Обновить
Я представил НКУ а также лязг сообщение об ошибке.
Отчет об ошибке clang был обновлен как исправленный:
Исправлено в r229792.
Отчет об ошибках в gcc обновлен как исправленный:
Исправлена.
и живой пример, кажется, подтверждает это.