如何使用嵌套枚举实现代码重用

时间:2013-03-01 11:43:09

标签: c++ inheritance c++11 enums code-reuse

我正在尝试构建一个类,使其子类具有C ++ 11 enum class属性,并具有关联的setter / getter:

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()派生的行为来使用该功能)。

有什么建议吗?

3 个答案:

答案 0 :(得分:1)

编译器是正确的:尽管枚举Base::MyEnumDerived::MyEnum是在通过继承连接的类中定义的,但枚举本身并不被认为是相关的。它们碰巧具有相同的非限定名称,这对编译器没有任何意义:就编译器而言,两个枚举类型是无关的。

如果你考虑枚举在引擎盖下实现的方式,这是有道理的:尽管强类型,枚举仍然是小的整数常量。由于两者不相关,Base::MyEnum::ADerived::MyEnum::C具有相同的值,并且在运行时没有任何东西可以让您区分这两个值。

除了将所有枚举值转储到基类的enum(这会将机会扩展到您自己的库之外)之外,您几乎无法做到:C++ enumerations do not support inheritance,并且通常不是很灵活。

答案 1 :(得分:1)

这是一个坏主意,不会起作用。

  • 语言级别您无法在C ++中重新定义内容。你只能隐藏(不完全)用同名的新东西,不相关的东西。您还可以通过为虚拟功能提供新的实现来覆盖虚拟功能,同时保留其签名。
  • 设计级别您的基类定义了派生类必须遵守的合同。如果Base::get()返回MyEnum {A, B},那么每个派生类必须有get(),并返回MyEnum {A, B}。这就是继承的全部内容(而不是代码重用,或者不仅仅是代码重用)。

您可以通过将类变为模板而不是依赖继承来实现代码重用。

答案 2 :(得分:1)

你可以使Base成为一个模板,由枚举类型参数化,并使用traits类提供一个“默认”枚举类型,可以通过派生类型进行专门化。

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并将getter和setter移动到从Base派生的中间类模板来解决这个问题,然后派生类型从中派生出来。

class Base { ... };

template<typename T = void> class GetterSetterImpl : public Base { ... };

class Derived : public GetterSetterImpl<Derived> { ... };