viniendo de un fondo principalmente de Python, he tenido dificultades para trabajar con tipos en C ++.
Estoy intentando inicializar una variable de clase a través de uno de varios constructores sobrecargados que toman diferentes tipos como parámetros. He leído que usar la auto
palabra clave se puede usar para la declaración automática de una variable, sin embargo, en mi caso, no se inicializará hasta que se elija un constructor. Sin embargo, el compilador no está contento con no inicializar value
.
class Token {
public:
auto value;
Token(int ivalue) {
value = ivalue;
}
Token(float fvalue) {
value = fvalue;
}
Token(std::string svalue) {
value = svalue;
}
void printValue() {
std::cout << "The token value is: " << value << std::endl;
}
};
En python esto podría verse así:
class Token():
def __init__(self, value):
self.value = value
def printValue(self):
print("The token value is: %s" % self.value)
¿Cuál es la forma correcta de usar la auto
palabra clave en este escenario? ¿Debo usar un enfoque completamente diferente?
auto
para los miembros de la clase? Pregunta relevante pero desactualizada: ¿Es posible tener una variable miembro "auto"?Respuestas:
No existe tal cosa como "variable de tipo desconocido" en C ++.
Las variables auto deducidas tienen un tipo que se deduce del inicializador. Si no hay inicializador, no puede usar auto. auto no se puede usar para una variable miembro no estática. Una instancia de una clase no puede tener miembros con un tipo diferente que otra instancia.
No hay forma de usar la palabra clave automática en este escenario.
Probablemente. Parece que estás intentando implementar a
std::variant
. Si necesita una variable para almacenar uno de X número de tipos, eso es lo que debe usar.Sin embargo, puede estar tratando de emular la escritura dinámica en C ++. Si bien puede ser familiar para usted debido a la experiencia con Python, en muchos casos ese no es el enfoque ideal. Por ejemplo, en este programa de ejemplo en particular, todo lo que hace con la variable miembro es imprimirlo. Por lo tanto, sería más sencillo almacenar una cadena en cada caso. Otros enfoques son el polimorfismo estático como lo muestra Rhathin o el polimorfismo dinámico de estilo OOP como lo muestra Fire Lancer.
fuente
union
es un mecanismo de bajo nivel propenso a errores.variant
probablemente lo usa internamente y hace que su uso sea más seguro.variant
hace usounion
. La alternativa, usando memoria en bruto y ubicación nueva, no puede usarse en unconstexpr
constructor.C ++ es un lenguaje de tipo estático , lo que significa que todos los tipos de variables se determinan antes del tiempo de ejecución. Por lo tanto, la
auto
palabra clave no es algo así como lavar
palabra clave en javascript, que es un lenguaje de tipo dinámico.auto
La palabra clave se usa comúnmente para especificar tipos que son innecesariamente complejos.Lo que está buscando podría hacerse utilizando la clase de plantilla C ++, que permite crear múltiples versiones de la clase que toman diferentes tipos.
Este código puede ser la respuesta que estás buscando.
Este código se compilaría si se cumplen algunas condiciones, como la función
operator<<
debería definirse para std :: ostream & y type T.fuente
Un enfoque diferente, de lo que otros han propuesto, es usar plantillas. Aquí hay un ejemplo:
Entonces puedes usar tu clase así:
fuente
Puedes usar el
std::variant
tipo. El siguiente código muestra una forma (pero debo admitir que es un poco torpe):Sería mucho mejor si se
std::get<0>(value)
pudiera escribir comostd::get<value.index()>(value)
pero, por desgracia, la "x"<x>
debe ser una expresión constante en tiempo de compilación.fuente
std::visit
lugar deswitch
.auto
debe ser deducible a un tipo específico, no proporciona escritura dinámica en tiempo de ejecución.Si al momento de declarar
Token
conoce todos los tipos posibles que puede usar,std::variant<Type1, Type2, Type3>
etc. Esto es similar a tener una "enumeración de tipos" y una "unión". Se asegura de que se llame a los constructores y destructores adecuados.Una alternativa podría ser crear un
Token
subtipo diferente para cada caso (posiblemente usando plantillas) con métodos virtuales adecuados.fuente
La solución a continuación es similar en espíritu a la respuesta de Fire Lancer. Su diferencia clave es que sigue el comentario posiblemente usando plantillas , y así elimina la necesidad de crear instancias derivadas creadas explícitamente de la interfaz.
Token
no es en sí la clase de interfaz. En cambio, define la interfaz como una clase interna y una clase de plantilla interna para automatizar la definición de las clases derivadas.Su definición parece demasiado compleja. Sin embargo,
Token::Base
define la interfaz yToken::Impl<>
deriva de la interfaz. Estas clases internas están completamente ocultas para el usuario deToken
. El uso se vería así:Además, la solución a continuación ilustra cómo se podría implementar un operador de conversión para asignar una
Token
instancia a una variable regular. Se basadynamic_cast
y arrojará una excepción si el lanzamiento no es válido.La definición de
Token
está abajo.Pruébalo en línea!
fuente