c++ - tener - ¿Por qué un valor de una enumeración con un tipo fijo subyacente de char se resuelve en fct(int) en lugar de fct(char)?
que es un enum en c# (2)
De acuerdo con mi interpretación de la norma actual, la llamada debe ser ambigua . Sigue una explicación.
Según 4.5 / 4:
"Un prvalue de un tipo de enumeración sin ámbito cuyo tipo subyacente es fijo (7.2) se puede convertir en un prvalue de su tipo subyacente. Además, si la promoción integral se puede aplicar a su tipo subyacente, un prvalue de un tipo de enumeración sin ámbito cuyo tipo subyacente También se puede convertir a un valor predeterminado del tipo subyacente promovido ".
Esto ofrece dos promociones alternativas: una promoción al tipo subyacente y una promoción al tipo subyacente promovido . Por lo tanto, solo este párrafo introduce ambigüedad en cuanto a cuál de estas alternativas debe usarse al resolver una llamada de función a funciones sobrecargadas.
Luego, el párrafo 13.3.3 decide cuál es la mejor función viable de un conjunto de sobrecarga en términos de "secuencia de conversión" . En particular, relevante para este asunto es 13.3.3.1 (" Secuencias de conversión implícita ") y, más específicamente, 13.3.3.1.1 (" Secuencias de conversión estándar "), que define de qué pasos elementales están formadas estas secuencias de conversión.
13.3.3.1.1 / 1 y la Tabla 12 clasifican estos pasos en cuatro categorías, entre las cuales Promoción y Conversión , y clasifican las secuencias de conversión según la categoría de conversiones individuales que conforman esas secuencias.
En nuestro caso, tenemos dos secuencias de conversión de una sola longitud compuestas por un solo paso de Promoción (ambos permitidos por 4.5./4).
Las secuencias de conversión se clasifican de acuerdo con 13.3.3.2. En particular, 13.3.3.2/3 menciona que una secuencia de conversión S1 es preferible a una secuencia de conversión S2 si:
"el rango [es decir, Promoción, Conversión, etc.] de S1 es mejor que el rango de S2, o S1 y S2 tienen el mismo rango y son distinguibles por las reglas en el párrafo a continuación , o, si no eso, [.. .] "
El " párrafo a continuación " que se menciona es 13.3.3.2/4, que dice:
"Las secuencias de conversión estándar están ordenadas por sus rangos: una concordancia exacta es mejor que una promoción, que es una conversión mejor que una conversión. Dos secuencias de conversión con el mismo rango son indistinguibles a menos que se aplique una de las siguientes reglas :"
Entonces, lo que sigue es un conjunto de reglas que no se aplican en nuestra situación.
Por lo tanto, tenemos dos secuencias de conversión de un solo paso formadas por una única promoción con el mismo rango. De acuerdo con la interpretación anterior de la norma actual, la llamada debe ser ambigua .
Personalmente, sin embargo, estoy de acuerdo en que se necesita un cambio para hacer que la conversión al tipo subyacente fijo de una enumeración sin ámbito sea preferible a otras posibles conversiones.
Este problema surgió al responder esta pregunta sobre resolución de sobrecarga con enumeraciones .
Si bien el caso durante long long
definitivamente fue un error en MSVC2012NovCTP (de acuerdo con el texto estándar y una prueba con gcc 4.7.1), no puedo entender por qué ocurre el siguiente comportamiento:
#include <iostream>
enum charEnum : char { A = ''A'' };
void fct(char) { std::cout << "fct(char)" << std::endl; }
void fct(int) { std::cout << "fct(int)" << std::endl; }
void fct(long long) { std::cout << "fct(long long)" << std::endl; }
int main()
{
fct(''A'');
fct(A);
}
Tanto MSVC2012NovCTP como gcc 4.7.1 están de acuerdo con esta salida:
fct (char)
fct (int)
¿No debería A
convertirse de charEnum
a char
? ¿Por qué se está convirtiendo A a int
?
EDITAR: clang se queja de que la llamada es ambigua, lo que concuerda con mi interpretación a continuación; Dicho esto, todavía lo encontraría mucho más intuitivo si solo se considerara como el tipo subyacente.
Dos extractos estándares relevantes son §7.2 / 9:
El valor de un enumerador o un objeto de un tipo de enumeración sin ámbito se convierte en un entero por promoción integral (4.5)
Y §4.5 / 4:
Un prvalue de un tipo de enumeración sin ámbito cuyo tipo subyacente es fijo (7.2) se puede convertir en un prvalue de su tipo subyacente. Además, si la promoción integral se puede aplicar a su tipo subyacente, un prvalor de un tipo de enumeración sin ámbito cuyo tipo subyacente se fije también se puede convertir a un prvalor del tipo subyacente promovido.
Por charEnum
tanto, charEnum
se puede convertir a char
, o cualquier promoción integral de char
, como int
.
Pero esto es vago para mí porque "puedo" no dice exactamente cuál será elegido realmente. En todo caso, esto debería ser ambiguo con esta redacción porque no se da ninguna preferencia entre char
o cualquiera de sus promociones. Si comenta fct(int)
, entonces la llamada es ambigua. ¿Por qué es especial la int
?
Lo único en lo que puedo pensar es que las promociones integrales se aplican recursivamente, pero nada de lo que veo lo encomienda.
En C ++ 03, la regla era:
Un valor de un tipo de enumeración sin ámbito (7.2 [dcl.enum]) se puede convertir en un valor de los primeros de los siguientes tipos que pueden representar todos los valores de la enumeración (es decir, los valores en el rango de bmin a bmax como se describe en 7.2 [dcl.enum]): int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int.
En un compilador de C ++ 03, se elegirá int
porque es el primero en la lista.
En C ++ 11, se introdujo el tipo subyacente . En consecuencia, a través de 685. La promoción integral de la enumeración ignora el tipo subyacente fijo , esta redacción se cambió al párrafo que citó en §4.5 / 4 y, al leer el informe de defectos, parece que la intención del comité fue para fct(char)
(el tipo subyacente) a elegir.
Sin embargo, de acuerdo con la discusión bajo el problema central 1601 , el texto en C ++ 11 en realidad hace que la conversión sea ambigua ( fct(char)
y fct(int)
son ambas posibles y ninguna es la preferida).
La siguiente solución fue propuesta y aceptada en C ++ 14:
Una conversión que promueva una enumeración cuyo tipo subyacente esté fijo a su tipo subyacente es mejor que una que promueva al tipo subyacente promovido, si los dos son diferentes.
Como se informó de un defecto en C ++ 11, los compiladores deberían aplicar esta corrección en el modo C ++ 11 y llamar a fct(char)
.