para - enum type c++ example
AsignaciĆ³n incorrecta de valores en caracteres. (5)
Estaba jugando con enums e intenté reproducir algunos ejemplos de esta página. Los ejemplos iniciales funcionaron según lo previsto, sin embargo obtuve algunos resultados interesantes con el siguiente código:
#include <iostream>
enum num : char {
zero = ''0'',
one = ''1'',
two = ''2'',
three = ''3'',
four = ''4'',
five = ''5'',
six = ''6''
};
int main()
{
const char two = ''2'';
std::cout << two << std::endl;
std::cout << num::two;
return 0;
}
La salida es:
2
50
Esperaba que ambos resultados fueran iguales, pero el num::two
parece imprimir algún otro valor. Además, este valor no cambia (50)
, así que asumo que no es un valor aleatorio / basura y que se está realizando algún tipo de análisis de char / int que no entiendo. Aquí está el enlace ideone .
Sé que puedo hacer que funcione asignando así zero = 0
, sin comillas simples y funciona. Sin embargo, quiero saber qué sucede detrás de escena y cómo puedo controlar qué valor de dígitos no únicos puedo imprimir a través de asignaciones de comillas simples.
Cuando dice enum num : char
, entonces expresa el hecho de que num
se implementa internamente en términos de un char
pero aún puede convertirse automáticamente en un valor entero, que no es necesariamente char
.
Como dice la página que cites:
Los valores de tipo de enumeración sin ámbito son convertibles implícitamente a tipos integrales.
Consulte ¿Por qué un valor de una enumeración con un tipo subyacente fijo de char se resuelve en fct (int) en lugar de fct (char)? para una interesante discusión sobre los problemas en la redacción de la norma C ++ con respecto a la combinación de promoción integral y tipos subyacentes fijos.
En cualquier caso, puede imaginar todo esto como una clase con una variable de miembro char
privada y un operador de conversión de int
público:
// very similar to your enum:
class num {
private:
char c;
public:
num(char c) : c(c) {}
operator int() const {
return c;
}
};
num two { ''2'' };
std::cout << two; // prints 50
Para aumentar la seguridad de tipos y hacer que la línea std::cout
un error de compilación, simplemente convierta la enum
en una enum class
:
enum class num : char
Esto es de nuevo similar al class num
imaginado arriba, pero sin el operador de conversión.
Cuando alimenta una instancia de num
a std::cout
, entonces es un cliente de num
y no se supone lógicamente que piense que el formato de salida tendrá en cuenta su implementación de caracteres internos.
Para obtener más control sobre el formato de salida, debe hacer lo mismo con cualquier otro tipo personalizado, y sobrecargar el operator<<
para std::ostream
. Ejemplo:
#include <iostream>
enum class num : char {
zero = ''0'',
one = ''1'',
two = ''2'',
three = ''3'',
four = ''4'',
five = ''5'',
six = ''6''
};
std::ostream& operator<<(std::ostream& os, num const& n)
{
switch (n)
{
case num::zero: os << "Zero"; break;
case num::one: os << "One"; break;
case num::two: os << "Two"; break;
case num::three: os << "Three"; break;
// and so on
}
return os;
}
int main()
{
std::cout << num::two; // prints "Two"
}
Por supuesto, los valores de char
específicos de las instancias de enumeración ahora se han vuelto bastante inútiles, por lo que también puede deshacerse de ellos por completo:
enum class num : char {
zero,
one,
two,
three,
four,
five,
six
};
Esto puede parecer extraño, pero tenga en cuenta que una enumeración que no representa más que números genéricos de cero a seis no es un caso de uso realista.
Debido a que los dos llaman a dos sobrecargas de operadores diferentes:
el primero llama al
operator<<
no miembrooperator<<
parastd::ostream
ychar
. Esto imprime el personaje.El segundo ejemplo llama al
operator<<
miembrooperator<<
paraint
debido a las promociones de enteros, como se explica en otras respuestas.
Esto debería ir a la sobrecarga de char
ahora; desafortunadamente ninguno de los compiladores en cuestión implementa el DR 1601 .
Un prvalue de un tipo de enumeración sin ámbito cuyo tipo subyacente es fijo ([dcl.enum]) se puede convertir a un prvalue de su tipo subyacente.
Esto significa que num
puede ser promovido a char
.
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 es fijo también se puede convertir a un prvalue del tipo subyacente promovido.
Así que num
puede ser promovido a int
, también.
Los candidatos relevantes son:
template <class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
char ch );
template<class charT, class Traits>
basic_ostream<charT, Traits>& basic_ostream<charT, Traits>::operator<<(int);
Para ambos candidatos, el primer argumento es una conversión de identidad y el segundo es una promoción. Tanto num
para char
como num
para tener rango de promoción.
Pre-DR1601, son igual de buenos, por lo que se incluye el desempate de plantilla / no plantilla. El primero es una plantilla de función; el segundo es una función miembro simple, por lo que el segundo gana.
DR1601 agregó una regla que dice:
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.
Esto significa que num
a char
ahora es mejor que num
a int
, por lo que la primera sobrecarga ahora es una mejor coincidencia y debe seleccionarse.
La razón es que su enum : char
no es lo mismo que char
(que es exactamente lo que queremos, no queremos que la enumeración sea igual a otros tipos, incluso si son compatibles con la asignación), queremos la void func(num n)
para ser distinto de void func(char n)
, ¿verdad?).
Por lo tanto, como enum num
no es un char
, se utilizará el operator<<(int)
e imprime el valor entero, incluso si el tipo subyacente es char
. No del todo sensible, pero estoy seguro de que esto es lo que sucede.
Según el estándar C ++ (4.5 promociones integrales)
4 Un prvalue de un tipo de enumeración sin ámbito cuyo tipo subyacente es fijo (7.2) se puede convertir a 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 es fijo también se puede convertir a un prvalue del tipo subyacente promovido .
Por lo tanto, se aplica la promoción integral y se llama al operador << para objetos de tipo int.