14

Base が基本クラスである継承チェーンがあります。Base および可能な別の Base 派生クラスを継承するクラス テンプレートを作成できるようにしたいと考えています。仮想継承を使用することもできましたが、別の解決策を見つけました。それが一般的/かなりの/正当なクラス設計であるかどうかを知りたい:

テンプレート パラメータが派生元のクラスであるクラス テンプレートを記述します。つまり、Base または Base 派生クラスである必要があります。コンストラクターでは、静的アサートを使用して、ユーザーが不正なクラスをテンプレート パラメーターとして使用していないことを確認できます。

それが機能する場合、仮想継承の問題は発生しません...問題は、それを実行しても問題ないということです。他のプロジェクトでは見たことがないので、使用する前に確認したいと思います。

編集:混乱しないようにするために、ここにいくつかのコードがあります:

class Base
{
};

class Derived : public Base
{
};

template <Class TheBase>
class MyDerived : public TheBase
{
};

Baseこれで、または のBase派生クラス、たとえばをパラメーターDerivedとして使用できるようになりました。TheBase

4

6 に答える 6

23

これは有効な設計パターンです。CRTP ではなく mixin の継承です。Mixin 継承は、プログラマーが手動で継承階層を線形化することにより、多重継承を安全にシミュレートする方法を提供します。テンプレート化されたクラスはミックスインです。複数の mixin でクラスを拡張する場合は、 のように構成の順序を決定する必要がありBig<Friendly<Dog> >ます。C++ での Mixin プログラミングについては、このDobb 博士の記事で説明されています。ここで説明されているように、Mixin を使用して静的バージョンの GoF Decorator パターンを実装できます。ミックスインは、Scala と SmallTalk でトレイト (C++ トレイトではなく) が果たすのと同様の役割を C++ で果たします。

CRTPでは、テンプレートとなるのは基本クラスです。

template <class Param>
class Base { ... };

class Derived : public Base<Derived> { ... };
于 2014-01-29T07:31:27.953 に答える
10

その後の編集:1年後、ここで私は自分の答えを修正しています。私は当初、OP が投稿したパターンは CRTP であると誤って述べました。これは正しくありません。これは確かに mixin です。正しい説明については、ページ下部の Daniel Mahler の回答をお読みください。

原文:こんなデザインでもいいです。たとえば、WTLはこれを使用します。静的ポリモーフィズムを実装するために使用され、 Curiously recurring テンプレート パターンと呼ばれます

于 2013-03-20T17:44:32.350 に答える
8

ザディリオンが指摘するように、これで問題ありません。それが機能する理由 (簡略化) は、C# のジェネリックとは異なり、C++ のテンプレートはコンパイル時であるためです。「それは typedef です」と言うのは私の怠慢であり、それに対して多くの非難を受けるでしょうが、単純にして、そうだったとしましょう。

検討:

class base {
protected:
    base() { };
    virtual ~base() { };
};

template<class T>
class super : public T {
};

以降:

super<base> s;

絶対に大丈夫です。これは実際にはかなり美しい構造です。コンパイル時であるため、基本クラスを選択できます。これは、一部の設計イディオムでは非常に有利な場合があります。

于 2013-03-20T17:47:58.313 に答える
5

良いモットーは次のとおりです。型にはテンプレートを使用しますが、動作には継承を使用します。

それに固執。作業を完了するために使用できるショートカットやトリックは確かにたくさんありますが、長い目で見れば、これらの悪い設計の選択は頭痛の種になります。そのようなものを使用したい場合は、必ず利点と欠点を調査してください。

さて、あなたの質問に戻りますが、あなたが尋ねたことは可能です: CRTPStatic polymorphismを参照してください。

于 2013-03-20T17:47:35.053 に答える
4

有効な C++ であるCuriously Recurring Template Patternについて話しているようです。

于 2013-03-20T17:46:22.847 に答える
1

あなたがしようとしているのは、共通の基本クラスを持つ可能性のある 2 つのクラスから継承することです。それは正しいですか? その場合、仮想継承の問題に対処する必要があります (つまり、関心のある 2 つのクラスの両方について、基本クラスの継承を仮想として宣言する必要があります)。これは、いくつかのランタイム サポート (2 つの vpointers 以上) により、わずかな (おそらく取るに足らない) オーバーヘッドを引き起こすだけです。

あなたのコードは CRTP ではなく (CRTP では、基本クラスは派生クラスを受け取るテンプレート化されたものです)、取り除こうとしていた二重継承の問題にまったく対処していないようです。

私が見る限り、仮想継承を受け入れて最小限のオーバーヘッドで発生するvirtualキーワードを使用するか、コードをリファクタリングすることができます。

あなたが何をしようとしているのか完全には理解できませんでしたが、共通の基本クラスを持つ2つの異なるクラスから継承しようとしている場合(仮想継承はこれに関するすべてです)、何らかの理由でvirtualキーワードの場合、CRTP を次のように使用できます。

#include <iostream>
using namespace std;

template<class Derived>
class Base
{
public:
    void basefunc() { cout << "base here"<< endl; }
    virtual void polyfunc() { cout << "base poly here"<< endl; }
};

class Derived : public Base<Derived>
{
public:
    void derivedfunc() { cout << "derived here"<< endl; }
    virtual void polyfunc() { cout << "derived poly here"<< endl; }
};

class OtherDerived : public Base<OtherDerived>
{
public:
    void otherderivedfunc() { cout << "otherderived here"<< endl; }
    virtual void polyfunc() { cout << "otherderived poly here"<< endl; }
};

class InheritingFromBoth : public Derived, public OtherDerived
{
public:
    void inheritingfunc() { cout << "inheritingfromboth here" << endl; }
    virtual void polyfunc() { cout << "inheritingfromboth poly here"<< endl; }  
};

int main() {

    Derived obj;
    OtherDerived obj2;

    InheritingFromBoth *obj3 = new InheritingFromBoth();
    Derived *der = dynamic_cast<Derived*>(obj3);
    der->polyfunc();
    OtherDerived *der2 = dynamic_cast<OtherDerived*>(obj3);
    der2->polyfunc();

    Base<Derived>* bptr = dynamic_cast<Base<Derived>*>(obj3);
    bptr->polyfunc();
    Base<OtherDerived>* bptr2 = dynamic_cast<Base<OtherDerived>*>(obj3);
    bptr2->polyfunc();



    return 0;
}

基本クラスの 2 つの異なるインスタンスを作成することで、継承のあいまいさを回避できます。

基本クラスと基本派生クラスから同時に継承する場合の、より単純で、おそらくよりクリーンな解決策は次のとおりです。

  • クラス BaseとBase 派生クラスから継承すると、Base メソッドと Base 派生メソッドに同時にアクセスできるようになりたいと思います..

クラスの設計で名前を隠す潜在的な 問題に注意を払い、ポリモーフィズムを使用して、実際に変更したい関数の動作を「カスタマイズ」する(キャスト制御できる)場合、Base -> Derived -> YourClass最終的に解決できるクリーンな階層あなたの問題。

あなたの特定のケースでは、多くのアプリケーションで使用されていると他の人が指摘しているように、あなたのアプローチは機能しますが、二重継承の問題を効果的に解決できるとは思いません。最終的には、特定の設計ケースのみが、それほど害のないソリューションにつながる可能性があります。

于 2014-01-30T23:46:02.323 に答える