1

我正在尝试构建一个类,使其子类具有 C++11enum 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/attribute:

// 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;
}

我了解问题所在;我只是在寻找能够在这种情况下重用代码的最干净的方法。

最好仅通过继承(或者更确切地说,该功能应该仅通过从 Base() 派生的行为对 Derived() 类可用)。

有什么建议么?

4

3 回答 3

1

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

如果您考虑枚举在底层实现的方式,这是有道理的:尽管是强类型的,但枚举仍然是小的整数常量。由于两者不相关,Base::MyEnum::A因此具有与 相同的值Derived::MyEnum::C,并且在运行时不会有任何内容可以让您区分这两个值。

除了将所有枚举值转储到enum基类中(这会扼杀在您自己的库之外扩展的机会),您几乎无能为力:C++ 枚举不支持继承,而且通常不是很灵活。

于 2013-03-01T12:06:24.523 回答
1

您可以制作Base一个模板,由枚举类型参数化,并使用一个特征类来提供一个可以由派生类型专门化的“默认”枚举类型。

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> { ... };
于 2013-03-01T12:29:46.617 回答
1

这是个坏主意,而且行不通。

  • 语言级别你不能用 C++ 重新定义东西。您只能(不完全)将内容隐藏在具有相同名称的新的、不相关的内容后面。您还可以通过给它一个新的实现来覆盖一个虚函数,同时保留它的签名。
  • 设计级别您的基类定义了派生类必须遵守的契约。如果Base::get()返回 a MyEnum {A, B},那么每个派生类都必须有一个get()返回 a 的 a MyEnum {A, B}。这就是继承的全部意义(而不是代码重用,或者无论如何不仅仅是代码重用)。

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

于 2013-03-01T12:13:26.413 回答