Pregunta 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, ya que requiere un llamado reducción de la conversión 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 objetivo 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 objetivo y producirá el valor original cuando convertido de nuevo al tipo original.

76
2017-12-13 22:33


origen


Respuestas:


Me encontré con este cambio radical cuando utilicé GCC. El compilador 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 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 a unsigned int dentro { }

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<unsigned int>(i & 0xFFFFFFFF),
            static_cast<unsigned int>(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 podrían obtener  confuso, aunque.


41
2018-01-14 12:25



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 compiladores habrían arrojado 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 pueden aparecer un poco más a menudo que tu tipo flotante al ejemplo de tipo integral.


9
2017-12-13 22:49



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 compilarse (y funcionar, siempre que la pérdida de precisión esté dentro de una precisión aceptable para el programa), 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.


7
2017-11-03 19:13



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 double que causa promoción.


5
2018-03-23 10:02



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 c y d implícitamente ser promovido a int y el resultado int no se puede volver a reducir 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<char c, char d>
void function() {
    char_t a = { c+d };
}

está bien y se compila sin una advertencia si la suma 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) me gusta 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).


4
2018-05-20 14:56



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

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

2
2018-05-29 08:49



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


1