¿Hay algún patrón en el que pueda heredar enum de otra enumeración en C++?Base enum class inheritance
Algo así:
enum eBase
{
one=1, two, three
};
enum eDerived: public Base
{
four=4, five, six
};
¿Hay algún patrón en el que pueda heredar enum de otra enumeración en C++?Base enum class inheritance
Algo así:
enum eBase
{
one=1, two, three
};
enum eDerived: public Base
{
four=4, five, six
};
No es posible. No hay herencia con enums.
En su lugar, puede utilizar clases con const ints nombrados.
Ejemplo:
class Colors
{
public:
static const int RED = 1;
static const int GREEN = 2;
};
class RGB : public Colors
{
static const int BLUE = 10;
};
class FourColors : public Colors
{
public:
static const int ORANGE = 100;
static const int PURPLE = 101;
};
#include <iostream>
#include <ostream>
class Enum
{
public:
enum
{
One = 1,
Two,
Last
};
};
class EnumDeriv : public Enum
{
public:
enum
{
Three = Enum::Last,
Four,
Five
};
};
int main()
{
std::cout << EnumDeriv::One << std::endl;
std::cout << EnumDeriv::Four << std::endl;
return 0;
}
¡Estoy confundido! ¿Cómo se referiría entonces a los tipos Enum en un argumento variable o de función, y cómo se aseguraría de que una función que espera que Enum no reciba un EnumDeriv? –
Esto no funcionará. Cuando define algunas funciones 'int basic (EnumBase b) {return b; } 'y' int derived (EnumDeriv d) {return d; } ', esos tipos no serán convertibles a' int', aunque sí a las enumeraciones simples. Y cuando intentes incluso un código tan simple como este: 'cout << basic (EnumBase :: One) << endl;', entonces obtendrás un error: 'conversión de 'EnumBase ::
Imposible.
Pero puede definir la enumeración anónimamente en una clase, luego agregar constantes de enumeración adicionales en las clases derivadas.
No puede hacer eso directamente, pero podría intentar usar la solución del artículo this.
La idea principal es utilizar la clase de plantilla auxiliar que tiene valores enum y tiene el operador de conversión de tipo. Teniendo en cuenta que el tipo subyacente de enum es int
, puede usar esta clase de titular sin problemas en su código en lugar de la enumeración.
Si bien este fragmento de código puede resolver la pregunta, [incluyendo una explicación] (http://meta.stackexchange.com/questions/114762/explaining-entirely-coded-based-answers) realmente ayuda a mejorar la calidad de su enviar. Recuerde que usted está respondiendo la pregunta a los lectores en el futuro, y es posible que esas personas no sepan los motivos de su sugerencia de código. – NathanOliver
Esta es una gran respuesta; es una de esas instancias de "pensar en el problema de otra manera" y la idea de usar una plantilla realmente encaja. – JasonDiplomat
También eche un vistazo a algunas de las soluciones a estas plantillas de vis-a-vis: https://stackoverflow.com/questions/5871722/how-to-achieve-virtual-template-function-in-c – JasonDiplomat
Bueno, si defines enum
con el mismo nombre en la clase derivada y lo inicias desde el último elemento del correspondiente enum
en la clase base, recibirás casi lo que deseas - enum heredado. mirada a este código:
class Base
{
public:
enum ErrorType
{
GeneralError,
NoMemory,
FileNotFound,
LastItem
}
}
class Inherited: public Base
{
enum ErrorType
{
SocketError = Base::LastItem,
NotEnoughBandwidth,
}
}
mientras el el código es compilable, no podrá usarlo, ya que el compilador no podrá convertir de base :: ErrorType a Inherited :: ErrorType. – bavaza
@bavaza, seguro, debe usar un número entero en lugar de enumeraciones al pasar sus valores como parámetros. – Haspemulator
Como señaló bayda
, enum de no lo hacen (y/o no debería) tener una funcionalidad, así que he tomado la siguiente aproximación a su dilema mediante la adaptación de Mykola Golubyev
's respuesta :
typedef struct
{
enum
{
ONE = 1,
TWO,
LAST
};
}BaseEnum;
typedef struct : public BaseEnum
{
enum
{
THREE = BaseEnum::LAST,
FOUR,
FIVE
};
}DerivedEnum;
Hay pocos problemas con esta solución. En primer lugar, está contaminando BaseEnum con LAST, que en realidad no existe más que establecer el punto de inicio para DerivedEnum. En segundo lugar, ¿qué ocurre si quiero establecer explícitamente algunos valores en BaseEnum que colisionarían con los valores de DerivedEnum? De todos modos, eso es probablemente lo mejor que podemos hacer hasta ahora en C++ 14. –
Como dije, está adaptado de un ejemplo anterior, por lo que se expresa de la forma en que es completo, no me preocupa que el póster anterior tenga problemas lógicos en su ejemplo – vigilance
Desafortunadamente no es posible en C++ 14. Espero que tengamos esa función de lenguaje en C++ 17. Como ya tiene algunas soluciones para su problema, no proporcionaré una solución.
Me gustaría señalar que la redacción debe ser "extensión", no "herencia". La extensión permite más valores (cuando saltas de 3 a 6 valores en tu ejemplo), mientras que la herencia significa poner más restricciones a una clase base dada, por lo que el conjunto de posibilidades se reduce. Por lo tanto, el casting potencial funcionaría exactamente en oposición a la herencia. Puede convertir la clase derivada a la clase base y no viceversa con la herencia de clase. Pero cuando tienes extensiones "deberías" poder convertir la clase base a su extensión y no viceversa. Estoy diciendo "debería" porque, como dije, esa característica de idioma todavía no existe.
Tenga en cuenta que 'extends' es una palabra clave para herencia en el lenguaje de Eiffel. –
Tiene razón porque en este caso no se respeta el Principio de sustitución Liskov. El comitente no aceptará una solución que se parece a la herencia sintácticamente debido a esto. –
¿Qué tal esto? Ok, se crea una instancia para cada valor posible, pero además de eso es muy flexible. ¿Hay algún inconveniente?
.h:
class BaseEnum
{
public:
static const BaseEnum ONE;
static const BaseEnum TWO;
bool operator==(const BaseEnum& other);
protected:
BaseEnum() : i(maxI++) {}
const int i;
static int maxI;
};
class DerivedEnum : public BaseEnum
{
public:
static const DerivedEnum THREE;
};
.CPP:
int BaseEnum::maxI = 0;
bool BaseEnum::operator==(const BaseEnum& other) {
return i == other.i;
}
const BaseEnum BaseEnum::ONE;
const BaseEnum BaseEnum::TWO;
const DerivedEnum DerivedEnum::THREE;
Uso:
BaseEnum e = DerivedEnum::THREE;
if (e == DerivedEnum::THREE) {
std::cerr << "equal" << std::endl;
}
Las únicas desventajas que puedo ver son el mayor consumo de memoria y que necesita más líneas de código. Pero intentaré tu solución. – Knitschi
También hice '' '' BaseEnum :: i'''' public y '' 'BaseEnum :: maxI'''' private. – Knitschi
El constructor predeterminado protegido puede ser un problema cuando la enumeración necesita ser utilizada en macros o plantillas de terceros que requieren un constructor predeterminado. – Knitschi
Usted puede utilizar un proyecto para crear enumeraciones SuperEnum extensibles.
/*** my_enum.h ***/
class MyEnum: public SuperEnum<MyEnum>
{
public:
MyEnum() {}
explicit MyEnum(const int &value): SuperEnum(value) {}
static const MyEnum element1;
static const MyEnum element2;
static const MyEnum element3;
};
/*** my_enum.cpp ***/
const MyEnum MyEnum::element1(1);
const MyEnum MyEnum::element2;
const MyEnum MyEnum::element3;
/*** my_enum2.h ***/
class MyEnum2: public MyEnum
{
public:
MyEnum2() {}
explicit MyEnum2(const int &value): MyEnum(value) {}
static const MyEnum2 element4;
static const MyEnum2 element5;
};
/*** my_enum2.cpp ***/
const MyEnum2 MyEnum2::element4;
const MyEnum2 MyEnum2::element5;
/*** main.cpp ***/
std::cout << MyEnum2::element3;
// Output: 3
¿Hay algún problema con esta solución? Por ejemplo, (No tengo un conocimiento profundo del polimorfismo) ¿Sería posible tener un vector y usar, p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;? –
jespestana
@jespestana No, no usarás instancias de clase 'Colors'. Solo usa los valores int en los miembros static const. – jiandingzhe
Si te entiendo bien; entonces, tendría que usar un contenedor de vector. Pero aún podría escribir: p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;. ¿derecho? –
jespestana