Я пытаюсь построить класс так, чтобы его подклассы имели C ++ 11 enum class
атрибут со связанным установщиком / получателем:
class Base {
public:
enum class MyEnum {
A,
B
} my_enum;
Base() : my_enum(MyEnum::A) {}
MyEnum get() { return my_enum; }
void set(const MyEnum &ME) { my_enum = ME; }
};
class Derived : public Base {
public:
Derived(): Base() {
my_enum = MyEnum::B;
}
};
int main(int argc, char *argv[])
{
Derived Deriv;
// No problems!
Deriv.get() == Derived::MyEnum::B;
return 0;
}
Все идет нормально!
Однако я хотел бы, чтобы производные классы могли переопределять MyEnum
класс перечисления, при этом нет необходимости постоянно переопределять атрибут setter / getter /:
// Base as before
class Derived : public Base {
public:
enum class MyEnum {
C,
D
}; // intention: to override Base's "my_enum" attribute
Derived(): Base() {
my_enum = MyEnum::C;
// ERROR: cannot convert 'Derived::MyEnum' to 'Base::MyEnum'
}
};
int main(int argc, char *argv[])
{
Derived Deriv;
// ERROR: no match for 'operator==' for types 'Base::MyEnum' and 'Derived::MyEnum'
Deriv.get() == Derived::MyEnum::C;
return 0;
}
Я понимаю, в чем проблема; Я просто ищу самый чистый способ повторного использования кода для этого случая.
Желательно только через наследование (или, скорее, функциональность должна быть доступна для классов Derived () исключительно благодаря процессу наследования от Base ()).
Какие-либо предложения?
Вы могли бы сделать Base
шаблон, параметризованный типом enum и использующий класс признаков для предоставления типа enum «по умолчанию», который может быть специализирован для производных типов.
template<typename T>
struct MyEnumTraits
{
enum class type {
A,
B
};
static const type default_value = type::A;
};
template<typename T = void>
class Base {
public:
typedef typename MyEnumTraits<T>::type MyEnum;
MyEnum my_enum;
Base() : my_enum(MyEnumTraits<T>::default_value) {}
MyEnum get() { return my_enum; }
void set(const MyEnum &ME) { my_enum = ME; }
};
class Derived;
template<>
struct MyEnumTraits<Derived>
{
enum class type {
C,
D
};
static const type default_value = type::C;
};
class Derived : public Base<Derived> {
// ...
Но теперь разные производные типы будут иметь разные базовые классы, что, вероятно, не то, что вам нужно. Вы можете решить это, сохранив не шаблон Base
и перемещая метод получения и установки в шаблон промежуточного класса, который является производным от Base
а затем производные типы происходят из этого.
class Base { ... };
template<typename T = void> class GetterSetterImpl : public Base { ... };
class Derived : public GetterSetterImpl<Derived> { ... };
Компилятор прав: хотя перечисления Base::MyEnum
а также Derived::MyEnum
определяются в классах, связанных через наследование, сами перечисления не считаются связанными. Они просто имеют одинаковые неквалифицированный имя, которое ничего не значит для компилятора: что касается компилятора, два типа enum не связаны.
Если вы думаете о том, как перечисления реализованы внутри, это имеет смысл: несмотря на строгую типизацию, перечисления остаются небольшими интегральными константами. Поскольку эти два не связаны, Base::MyEnum::A
будет иметь то же значение, что и Derived::MyEnum::C
и во время выполнения не будет ничего, что позволяло бы вам различать эти два значения.
Помимо сброса всех значений перечисления в enum
базового класса (который убивает возможности расширяться за пределы вашей собственной библиотеки) мало что можно сделать: Перечисления C ++ не поддерживают наследование, и, как правило, не очень гибкие.
Это плохая идея, и она не сработает.
Base::get()
возвращает MyEnum {A, B}
тогда каждый производный класс должен иметь get()
который возвращает MyEnum {A, B}
, Вот в чем заключается наследование (а не повторное использование кода или не просто повторное использование кода в любом случае).Возможно, вам удастся добиться повторного использования кода, превратив свой класс в шаблон, а не полагаясь на наследование.