1

enum classサブクラスがC++11属性を持ち、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し、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、ゲッターとセッターをBase、派生型と派生型が派生する中間クラステンプレートに移動することで、これを解決できます。

class Base { ... };

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

class Derived : public GetterSetterImpl<Derived> { ... };
于 2013-03-01T12:29:46.617 に答える
1

コンパイラは正しいです。列挙Base::MyEnumDerived::MyEnumは継承によって接続されたクラスで定義されますが、列挙自体は関連しているとは見なされません。それらはたまたま同じ非修飾名を持っています。これはコンパイラにとって何の意味もありません。コンパイラに関する限り、2つの列挙型は無関係です。

列挙型が内部で実装される方法を考えると、これは理にかなっています。強く型付けされているにもかかわらず、列挙型は小さな整数定数のままです。2つは無関係であるためBase::MyEnum::A、と同じ値Derived::MyEnum::Cになり、実行時に2つの値を区別できるものはありません。

すべての列挙値をenum基本クラスのにダンプする(これにより、独自のライブラリの外部に拡張する機会が失われます)以外に、できることはほとんどありません。C++列挙は継承をサポートしておらず、一般にあまり柔軟性がありません。

于 2013-03-01T12:06:24.523 に答える
1

これは悪い考えであり、機能しません。

  • 言語レベルC++ではものを再定義することはできません。同じ名前の新しい無関係なものの背後にあるものだけを(完全にではなく)隠すことができます。署名を保持したまま、仮想関数に新しい実装を与えることで、仮想関数をオーバーライドすることもできます。
  • 設計レベル基本クラスは、派生クラスが準拠しなければならないコントラクトを定義します。Base::get()を返す場合MyEnum {A, B}、すべての派生クラスには、get()を返すが必要MyEnum {A, B}です。それが継承のすべてです(コードの再利用ではなく、コードの再利用だけではありません)。

継承に依存する代わりに、クラスをテンプレートにすることで、コードの再利用を実現できる場合があります。

于 2013-03-01T12:13:26.413 に答える