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


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

int a[] = { 1.0 };

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


Para referencia, véase 8.5.4 / 6 de n3225

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

  • de un tipo de coma flotante a un tipo entero, o
  • de long double a double o float, o de doble a flotar, excepto cuando el origen es una expresión de la constante y el valor real después de la conversión está dentro del rango de valores que pueden ser representados (incluso si no se puede representar exactamente), o
  • de un tipo entero o tipo de enumeración sin ámbito a un tipo de coma flotante, excepto donde la fuente es una expresión constante y el valor real después de la conversión se ajustará al tipo de destino y producirá el valor original cuando se convierta de nuevo al tipo original, o
  • de un tipo entero o tipo enumeración sin ámbito a un tipo entero que no puede representar todos los valores del tipo original, excepto donde la fuente es una expresión constante y el valor real después de la conversión se ajustará al tipo de destino y producirá el valor original cuando se convierta de nuevo al tipo original.
Author: Johannes Schaub - litb, 2010-12-14

7 answers

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

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

En 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 de { }

Error: reducción de la conversión de (((long long unsigned int)i) >> 32) de long long unsigned int a unsigned int dentro de { }

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

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

El código estaba en una biblioteca externa, con solo dos ocurrencias en un archivo. No creo que el cambio radical afecte mucho al código. Los novatos podrían obtener confundido, sin embargo.

 41
Author: Timothy003,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:25

Me sorprendería y decepcionaría saber que cualquiera de los códigos C++ que escribí en los últimos 12 años tenía este tipo de problemas. Pero la mayoría de los compiladores habrían vomitado advertencias sobre cualquier "estrechamiento" en tiempo de compilació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 podrían aparecer un poco más a menudo que su ejemplo de tipo flotante a tipo integral.

 9
Author: aschepler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-13 22:49:13

No me sorprendería tanto si alguien fuera 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 convierten de nuevo a int / long, por lo tanto se están estrechando)

No recuerdo haber escrito esto. 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;? ¿Cuál es la motivación, sin embargo, por qué debería compilarse (y funcionar, siempre que la pérdida de precisión esté dentro de la precisión aceptable para el programa), mientras que float asfloat[] = {val1, val2}; no debería? De cualquier manera estoy inicializando dos flotadores de dos ints, es sólo que en un caso los dos flotadores pasan a ser miembros de un agregado.

Eso parece particularmente duro en los casos en que una expresión no constante resulta en una conversión de estrechamiento a pesar de que (en un implementación particular), todos los valores del tipo de origen 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 ningún error, presumiblemente la solución es siempre hacer la conversión explícita. A menos que esté haciendo algo extraño con macros, creo que un inicializador de matriz solo aparece cerca del tipo de la matriz, o al menos a algo que representa el tipo, que podría depender de un parámetro de plantilla. Así que un yeso debe ser fácil, si es prolijo.

 7
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-14 01:48:55

Un ejemplo práctico 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 double que causa la promoción.

 5
Author: Jed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-03 19:13:02

Los errores de conversión de estrechamiento interactúan mal con las reglas implícitas de promoción de enteros.

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

struct char_t {
    char a;
}

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

Que produce un error de conversión de estrechamiento (que es correcto de acuerdo con el estándar). La razón es que c y d, implícitamente, promueven a int y el resultado int no puede ser reducido de nuevo a char en una lista de inicializador.

OTOH

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

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

template<char c, char d>
void function() {
    char_t a = { c+d };
}

Está bien y compila sin advertencia si la suma de c y d es menor que CHAR_MAX. Todavía creo que esto es un defecto en C++11, pero la gente allí piensa lo contrario, posiblemente porque no es fácil de arreglar sin deshacerse de la conversión implícita de enteros (que es una reliquia del pasado, cuando la gente escribió código como char a=b*c/d y esperaba que funcionara incluso si (b*c) > CHAR_MAX) o errores de conversión de estrechamiento (que posiblemente sean algo bueno).

 4
Author: Gunther Piez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-14 20:46:12

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

CFLAGS += -std=c++0x -Wno-narrowing
 3
Author: Kukuh Indrayana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-20 14:56:13

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

 1
Author: kyku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-29 08:49:58