Почему `bool b = 2` работает хорошо, а `bool b = {2}` выдает предупреждение о сужающем преобразовании?

Использование инициализатора {} в C++11 для инициализации bool b = {2} приводит к следующему предупреждающему сообщению:

warning: narrowing conversion of ‘2’ from ‘int’ to ‘bool’ inside { } [-Wnarrowing]

Однако при использовании старого стиля bool b = 2 такой проблемы нет. В чем причина этого?


Обновление: я скомпилировал код, используя g++ -std=c++11, и он выдал мне предупреждение. Если я добавлю опцию -pedantic-errors, предупреждение станет ошибкой.


person tianz    schedule 02.11.2013    source источник
comment
Когда я действительно думаю об этом, хотя мы привыкли думать о anything not 0 и 0 как о true и false, насколько я могу судить, стандарт не обещает вам, что true и false будут 1 и 0 соответственно, он просто гарантирует конверсию. Поэтому, если вы собираетесь использовать литерал, используйте true и false, что никогда не будет сужающим преобразованием.   -  person Shafik Yaghmour    schedule 02.11.2013


Ответы (2)


Сужение типа данных в списке инициализации делает вашу программу на С++ 11 плохо сформированной, в этой ситуации компилятор может либо выдать предупреждение, либо продолжить работу.

Интересно, что вы действительно можете изменить его на bool b = {1}, и я предполагаю, что нет предупреждения, потому что значение bool гарантированно преобразуется в 0 и 1 в целочисленных типах.

Вот стандартная цитата, подтверждающая ошибку.

Сужающее преобразование — это неявное преобразование
— из типа с плавающей запятой в целочисленный тип или
— из long double в double или float или из double в float, за исключением случаев, когда источником является константное выражение и фактическое значение после преобразования находится в пределах диапазона значений, которые могут быть представлены (даже если оно не может быть представлено точно), или
— из целочисленного типа или типа перечисления с незаданной областью в тип с плавающей запятой, за исключением случаев, когда источник постоянное выражение и фактическое значение после преобразования будут соответствовать целевому типу и дадут исходное значение при преобразовании обратно в исходный тип, или
— из целочисленного типа или типа перечисления с незаданной областью в целочисленный тип, который не может представлять все значения исходного типа, за исключением случаев, когда источником является постоянное выражение, а фактическое значение после преобразования соответствует целевому типу и создает исходное значение при обратном преобразовании в исходный тип.
Как указано ab На самом деле, такие преобразования не разрешены на верхнем уровне в list-initializations.

person aaronman    schedule 02.11.2013
comment
bool b = {2} был разрешен, поскольку C++98 и сужающее преобразование не были ошибками в C++98. Если сделать это ошибкой в ​​C++11, это станет критическим изменением. Они действительно сделали это на C++11? Я правда не знаю, но подозреваю, что в случае bool b = {2} это не ошибка. Вероятно, поэтому в данном случае выдается простое предупреждение. (Поправьте меня если я ошибаюсь.) - person AnT; 02.11.2013
comment
@AndreyT это ошибка по крайней мере на clang, на gcc не проверял - person aaronman; 02.11.2013
comment
Хорошо, похоже, это действительно должна быть ошибка. Вероятно, они думали, что синтаксис {} очень редко использовался для инициализации скаляров (если вообще использовался), поэтому признание его ошибкой не нарушило бы большую часть устаревшего кода. - person AnT; 02.11.2013
comment
Я скомпилировал код, используя g++ -std=c++11, и вместо ошибки он выдал мне предупреждение. - person tianz; 02.11.2013
comment
@TianZhou должно быть ошибкой, вы уверены, что компилятор не игнорирует параметр -std=c++11 - person aaronman; 02.11.2013
comment
@TianZhou это бы сказало, какая у вас версия компилятора - person aaronman; 02.11.2013
comment
@AndreyT О, я только что добавил эту опцию, и предупреждение стало ошибкой! - person tianz; 02.11.2013
comment
@aaronman: значение bool в C++ не обязательно будет 0 или 1. Гарантировано преобразование в 0 или 1 при преобразовании в целочисленные типы. При этом внутреннее представление bool может быть любым. - person AnT; 02.11.2013
comment
@AndreyT достаточно честно, исправлено - person aaronman; 02.11.2013
comment
@TianZhou, это не так, старый IDT (сам не использую gcc), но, поскольку стандартная цитата в удаленном ответе говорит, что ошибка действительно верна, я тоже добавлю стандартную цитату в свой ответ. - person aaronman; 02.11.2013
comment
@ShafikYaghmour clang выдает ошибку, что мой gcc недостаточно актуален для ошибки - person aaronman; 02.11.2013
comment
@ShafikYaghmour Я исправлю свой ответ - person aaronman; 02.11.2013
comment
@aaronman Я могу ошибаться в неправильной форме, позвольте мне перечитать внимательнее. - person Shafik Yaghmour; 02.11.2013
comment
@aaronman Хорошо, похоже, неясно -according-to-th">что компилятор должен делать с неправильно сформированной программой, поэтому это, вероятно, означает, что любое поведение соответствует стандартам, что не очень удобно. - person Shafik Yaghmour; 02.11.2013
comment
@ShafikYaghmour, хотя этот случай неприменим, я предполагаю, что в некоторых случаях трудно или невозможно обнаружить неправильно сформированную программу, поэтому стандарт должен быть мягким - person aaronman; 02.11.2013
comment
Совершенно ясно, что соответствующий компилятор должен делать с плохо сформированной программой. Он должен диагностировать ошибку, что означает выдачу предупреждения или ошибки. Причина, по которой соответствующие компиляторы не обязаны отбрасывать неправильно сформированные программы, заключается в том, что они могут реализовывать расширения, оставаясь при этом соответствующими — до тех пор, пока расширения спроектированы таким образом, что любая программа, использующая их, является неправильно сформированной в соответствии со стандартом. - person Steve Jessop; 02.11.2013

Это выглядит как:

bool b = {2} ;

действительно является сужающим преобразованием, если мы посмотрим на черновик стандарта C++, раздел 8.5.4 Инициализация списка, параграф 7 гласит:

Сужающее преобразование — это неявное преобразование.

и включите следующий маркер (выделено мной):

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

bool не может представлять значение 2, так что это сужающее преобразование в самом строгом смысле. Что имеет смысл, весь смысл инициализации {} заключается в предотвращении неявных преобразований и увеличении безопасность ввода. На что опирается старый стиль, на который вы здесь ссылаетесь:

bool b = 2

который зависит от раздела 4.12 Булевы преобразования, в котором говорится:

[...] Нулевое значение, значение нулевого указателя или значение нулевого указателя элемента преобразуется в false; любое другое значение преобразуется в true. [...]

Конечно, вся посылка о том, что {2} будет сужающим преобразованием, основывается на предположении, что значения true и false равны 1 и 0, что, как и насколько я могу судить, не гарантируется в стандарте. Хотя подразумевается, что это единственное, что стандарт обещает в преобразованиях, но если мы собираемся использовать литерал, нам не нужно полагаться на это предположение, у нас есть два совершенно хороших логические литералы для этих true и false, которые вы должны использовать.

Для полноты картины, это сужающее преобразование неправильное, оно требует диагностики, поэтому либо предупреждение, либо ошибка приемлемо. если мы посмотрим на абзац 3, там сказано:

Список-инициализация объекта или ссылки типа T определяется следующим образом:

и включите следующий маркер (выделено мной):

В противном случае, если в списке инициализаторов есть один элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой на E, объект или ссылка инициализируется из этого элемента; если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа некорректна.

и включает следующий пример:

[ Example:
  int x1 {2}; // OK
  int x2 {2.0}; // error: narrowing
—end example ]
person Shafik Yaghmour    schedule 02.11.2013