Reducir las conversiones en C ++ 0x. ¿Soy solo yo, o suena esto como un cambio radical?

C ++ 0x va a hacer que el siguiente código y el código similar estén mal formados, porque requiere una llamada conversión de estrechamiento de un double a un int .

 int a[] = { 1.0 }; 

Me pregunto si este tipo de inicialización se usa mucho en el código del mundo real. ¿Cuántas claves se romperán con este cambio? ¿Es mucho esfuerzo arreglar esto en su código, si su código se ve afectado?


Para referencia, vea 8.5.4 / 6 de n3225

Una conversión de reducción es una conversión implícita

  • desde un tipo de punto flotante a un tipo entero, o
  • de largo doble a doble o flotante, o de doble a flotante, excepto cuando la fuente es una expresión constante y el valor real después de la conversión está dentro del rango de valores que se pueden representar (incluso si no se puede representar exactamente), o
  • desde un tipo entero o un tipo de enumeración no codificado a un tipo de punto flotante, excepto cuando el origen es una expresión constante y el valor real después de la conversión se ajustará al tipo objective y producirá el valor original cuando se vuelva a convertir al tipo original, o
  • desde un tipo entero o un tipo de enumeración no codificada a un tipo entero que no puede representar todos los valores del tipo original, excepto cuando el origen es una expresión constante y el valor real después de la conversión se ajustará al tipo objective y producirá el valor original cuando convertido de nuevo al tipo original.

Me encontré con este cambio radical cuando utilicé GCC. El comstackdor imprimió un error para un código como este:

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

En la función void foo(const long long unsigned int&) :

error: reducción de la conversión de (((long long unsigned int)i) & 4294967295ull) de long long unsigned int a unsigned int dentro {}

error: reducción de la conversión de (((long long unsigned int)i) >> 32) de long long unsigned int to unsigned int inside {}

Afortunadamente, los mensajes de error fueron claros y la solución fue simple:

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

El código estaba en una biblioteca externa, con solo dos apariciones en un archivo. No creo que el cambio de rotura afecte a muchos códigos. Los novatos pueden confundirse, sin embargo.

Me sorprendería y me decepcionaría saber que cualquier código C ++ que escribí en los últimos 12 años tuvo este tipo de problema. Pero la mayoría de los comstackdores habrían arrojado advertencias sobre cualquier “estrechamiento” en tiempo de comstackción todo el tiempo, a menos que me esté perdiendo algo.

¿Estas también están reduciendo las conversiones?

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

Si es así, creo que pueden aparecer un poco más a menudo que tu tipo flotante al ejemplo de tipo integral.

No me sorprendería mucho que alguien quede atrapado por algo como:

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

(En mi implementación, los dos últimos no producen el mismo resultado cuando se vuelven a convertir a int / long, por lo que se están reduciendo)

Sin embargo, no recuerdo haber escrito esto alguna vez. Solo es útil si una aproximación a los límites es útil para algo.

Esto parece al menos vagamente plausible también:

 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 } 

pero no es del todo convincente, porque si sé que tengo exactamente dos valores, ¿por qué ponerlos en matrices en lugar de simplemente float floatval1 = val1, floatval1 = val2; ? Sin embargo, cuál es la motivación por la que debería comstackrse (y funciona, siempre que la pérdida de precisión esté dentro de una precisión aceptable para el progtwig), mientras que float asfloat[] = {val1, val2}; no deberia? De cualquier manera estoy inicializando dos flotadores de dos entradas, es solo que en un caso los dos flotadores son miembros de un agregado.

Esto parece particularmente duro en los casos en que una expresión no constante da como resultado una conversión de estrechamiento aunque (en una implementación particular), todos los valores del tipo de fuente son representables en el tipo de destino y convertibles a sus valores originales:

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

Suponiendo que no hay errores, presumiblemente la solución es siempre hacer que la conversión sea explícita. A menos que esté haciendo algo extraño con las macros, creo que un inicializador de matriz solo aparece cerca del tipo de la matriz, o al menos de algo que representa el tipo, que podría depender de un parámetro de plantilla. Entonces un yeso debería ser fácil, si es detallado.

Una instancia práctica que he encontrado:

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

El literal numérico es implícitamente el double que causa la promoción.

El estrechamiento de los errores de conversión interactúa mal con las reglas de promoción de entero implícito.

Tuve un error con el código que parecía

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

Lo que produce un error de conversión de estrechamiento (que es correcto de acuerdo con el estándar). El motivo es que d repentinamente se promocionan a int y no se permite que el int resultante se reduzca a char en una lista de inicializadores.

OTOH

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

por supuesto, todavía está bien (de lo contrario, todo el infierno se desataría). Pero sorprendentemente, incluso

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

está bien y se comstack sin una advertencia si la sum de cyd es menor que CHAR_MAX. Todavía creo que esto es un defecto en C ++ 11, pero la gente piensa lo contrario, posiblemente porque no es fácil de arreglar sin deshacerse de cualquier conversión de entero implícita (que es una reliquia del pasado, cuando las personas escribían código) como char a=b*c/d y esperaba que funcionara incluso si (b * c)> CHAR_MAX) o reduciendo los errores de conversión (que posiblemente sean algo bueno).

Intente agregar -Wno-narrowing a su CFLAGS, por ejemplo:

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

Parece que GCC-4.7 ya no genera errores para reducir las conversiones, sino advertencias.

    Intereting Posts