Сужение конверсий в C ++ 0x. Это только я, или это звучит как нарушение?

C ++ 0x собирается сделать следующий код и аналогичный код плохо сформированным, потому что для этого требуется так называемое сужение преобразования double в int .

 int a[] = { 1.0 }; 

Мне интересно, используется ли эта инициализация в реальном мире. Сколько кода будет нарушено этим изменением? Много ли усилий исправить это в коде, если ваш код вообще затронут?


Для справки см. 8.5.4 / 6 of n3225

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

  • от типа с плавающей точкой до целочисленного типа или
  • от long double до double или float или от double до float, за исключением случаев, когда источник является постоянным выражением, а фактическое значение после преобразования находится в пределах диапазона значений, которые могут быть представлены (даже если они не могут быть представлены точно), или
  • от типа целочисленного типа или неперечисленного типа enums до типа флаговой точки, за исключением случаев, когда источник является константным выражением, а фактическое значение после преобразования будет соответствовать целевому типу и будет производить исходное значение при преобразовании обратно в исходный тип или
  • от целочисленного типа или неперечисленного типа enums до целочисленного типа, который не может представлять все значения исходного типа, за исключением случаев, когда источник является константным выражением, а фактическое значение после преобразования будет соответствовать целевому типу и будет производить исходное значение, когда преобразован обратно в исходный тип.

Я столкнулся с этим изменением, когда использовал GCC. Компилятор напечатал ошибку для кода следующим образом:

 void foo(const unsigned long long &i) { unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32}; } 

В функции void foo(const long long unsigned int&) :

ошибка: сужение преобразования (((long long unsigned int)i) & 4294967295ull) из long long unsigned int в unsigned int внутри {}

ошибка: сужение преобразования (((long long unsigned int)i) >> 32) из long long unsigned int в unsigned int внутри {}

К счастью, сообщения об ошибках были простыми, и исправление было простым:

 void foo(const unsigned long long &i) { unsigned int a[2] = {static_cast(i & 0xFFFFFFFF), static_cast(i >> 32)}; } 

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

Я был бы удивлен и разочарован в себе, узнав, что любой код C ++, который я написал за последние 12 лет, имел такую ​​проблему. Но большинство компиляторов изредка предупреждали о любых «сужениях» времени компиляции, если я ничего не пропущу.

Являются ли они также сужением конверсий?

 unsigned short b[] = { -1, INT_MAX }; 

Если это так, я думаю, что они могут появиться немного чаще, чем ваш тип плавающего типа для интегрального типа.

Я бы не удивился, если кто-нибудь поймает что-то вроде:

 float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX}; 

(при моей реализации последние два не дают одинакового результата при преобразовании обратно в int / long, следовательно, сужаются)

Однако я не помню, чтобы когда-либо писать это. Это полезно, только если приближение к пределам полезно для чего-то.

Это кажется, по крайней мере, смутно правдоподобным:

 void some_function(int val1, int val2) { float asfloat[] = {val1, val2}; // not in C++0x double asdouble[] = {val1, val2}; // not in C++0x int asint[] = {val1, val2}; // OK // now do something with the arrays } 

но это не совсем убедительно, потому что, если я знаю, что у меня есть ровно два значения, зачем класть их в массивы, а не просто float floatval1 = val1, floatval1 = val2; ? Какова мотивация, однако, почему это должно компилироваться (и работать, если потеря точности находится в пределах приемлемой точности для программы), а float asfloat[] = {val1, val2}; не надо? В любом случае я инициализирую два поплавка из двух ints, это просто, что в одном случае два поплавка являются членами совокупности.

Это кажется особенно суровым в случаях, когда неопределенное выражение приводит к сужению конверсии даже при том, что (при конкретной реализации) все значения типа источника представляются в типе назначения и возвращаются обратно к их исходным значениям:

 char i = something(); static_assert(CHAR_BIT == 8); double ra[] = {i}; // how is this worse than using a constant value? 

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

Практический пример, с которым я столкнулся:

 float x = 4.2; // an input argument float a[2] = {x-0.5, x+0.5}; 

Числовой литерал неявно double что вызывает продвижение по службе.

Сужение ошибок преобразования плохо связано с неявными целыми правилами продвижения.

У меня была ошибка с кодом, который выглядел как

 struct char_t { char a; } void function(char c, char d) { char_t a = { c+d }; } 

Который вызывает сужение ошибки преобразования (что соответствует стандарту). Причина в том, что c и d неявно получают повышение до int и результирующий int не может быть сужен назад к char в списке инициализаторов.

Ото

 void function(char c, char d) { char a = c+d; } 

конечно, все равно прекрасно (иначе все ад сломается). Но удивительно, что даже

 template void function() { char_t a = { c+d }; } 

нормально и компилируется без предупреждения, если сумма c и d меньше CHAR_MAX. Я все еще думаю, что это дефект в C ++ 11, но люди там думают иначе – возможно, потому что его нелегко исправить, не избавляясь от неявного целочисленного преобразования (которое является реликтовым из прошлого, когда люди пишут код как char a=b*c/d и ожидал, что он будет работать, даже если (b * c)> CHAR_MAX) или сужения ошибок преобразования (что, возможно, хорошо).

Попробуйте добавить -Wno-сужение к вашим CFLAGS, например:

 CFLAGS += -std=c++0x -Wno-narrowing 

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

Давайте будем гением компьютера.