Сужающие преобразования в C++0х. Это только мне кажется, или это похоже на критическое изменение?
C++0x собирается сделать следующий код и подобный код плохо сформированным, потому что он требует так называемого сужающее преобразование на double
до int
.
int a[] = { 1.0 };
мне интересно, используется ли этот вид инициализации в реальном коде. Сколько кода будет нарушено этим изменением? Это много усилий, чтобы исправить это в вашем коде, если ваш код влияет на все?
Для справки см. 8.5.4 / 6 из n3225
сужающее преобразование является неявным преобразованием
- от типа с плавающей запятой к целочисленному типу, или
- от long double к double или float, или от double к float, за исключением случаев, когда источник является постоянным выражением, а фактическое значение после преобразования находится в диапазоне значений, которые могут быть представлены (даже если оно не может быть представлено точно), или
- от целочисленного типа или типа перечисления без области видимости до типа с плавающей запятой тип, за исключением случаев, когда источником является константное выражение и фактическое значение после преобразования будет соответствовать целевому типу и будет производить исходное значение при преобразовании обратно в исходный тип, или
- из целочисленного типа или незаданной перечисления типа в целочисленный тип, который может представлять все значения исходного типа, за исключением случаев, когда источником является константным выражением, значение после преобразования в целевой тип и оригинал значение при преобразовании обратно в исходный тип.
7 ответов:
я столкнулся с этим критическим изменением, когда я использовал 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<unsigned int>(i & 0xFFFFFFFF), static_cast<unsigned int>(i >> 32)}; }
код был во внешней библиотеке, только с двумя вхождениями в одном файле. Я не думаю, что критическое изменение повлияет на объем кода. Новички могут getперепутал, хотя.
Я был бы удивлен и разочарован в себе, узнав, что любой из кода 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 в списке инициализаторов.OTOH
void function(char c, char d) { char a = c+d; }
в конечно, все еще хорошо (иначе весь ад вырвался бы на свободу). Но удивительно, даже
template<char c, char d> void function() { char_t a = { c+d }; }
нормально и компилируется без предупреждения, если сумма c и d меньше CHAR_MAX. Я все еще думаю, что это дефект в C++11, но люди там думают иначе - возможно, потому, что его нелегко исправить, не избавившись от любого неявного целочисленного преобразования (которое является реликтом из прошлого, когда люди писали код как
char a=b*c/d
и ожидал, что он будет работать, даже если (b*c) > CHAR_MAX) или сужение ошибки преобразования (что, возможно, хорошо).