2017-12-10 11 views
4

CRTPを使用する基本クラスがあると、型が派生クラスに依存する基本テンプレートクラスのメンバーを宣言しています。意図したように、次の作品ながら型が派生クラスに依存する基本テンプレートクラスのメンバーを宣言するにはどうすればよいですか?

template <class T> class BaseTraits; 
template <class T> class Base { 
    using TypeId = typename BaseTraits<T>::TypeId; 
    TypeId id; 
public: 
    Base() { id = 123; } 
    TypeId getId() { return id; } 
}; 

class Derived; 
template <> class BaseTraits<Derived> { 
public: 
    using TypeId = int; 
}; 

class Derived : public Base<Derived> {}; 

int main(int argc, char ** argv) { 
    Derived foo; 
    return foo.getId(); 
} 

私は実装を簡素化することができれば、私は疑問に思います。 Baseテンプレートに2番目のテンプレートパラメータを追加して、BaseTraitsを簡単にしたり、取り除いたりすることもできます。しかし、上記のスニペットはすでに第2のテンプレートパラメータを削除しようとしています。私はBaseの2番目のテンプレートパラメータを含まないソリューションを探しています。

私は次のようなものを試してみたが、それはコンパイルされません:

error: invalid use of incomplete type 'class Derived'

template <class T> class Base { 
    using TypeId = typename T::TypeId; 
    TypeId id; 
public: 
    Base() { id = 123; } 
    TypeId getId() { return id; } 
}; 

class Derived : public Base<Derived> { 
public: 
    using TypeId = int; 
}; 

int main(int argc, char ** argv) { 
    Derived foo; 
    return foo.getId(); 
} 

UPDATE:

  • 私はC++ 14に制限されています。
  • Baseはテンプレートである必要があります。
  • パフォーマンスは必須です。
+0

十分に宣言される前にクラスをテンプレートパラメータとして使用できないため、2番目の例はうまくいきません。あなたはそれらのクラスで何を達成したいですか?あなたは彼らがテンプレート化される必要があると確信していますか? –

+0

@MarošBeťkoこれは不完全なクラスなので、不完全なクラスをテンプレートパラメータとして使用できます。 –

+0

@MarošBeťkoBaseはDerivedのみがその型を知っているメンバーを宣言したいと思います。はい、私はテンプレートを使う必要があります。 – Flyer

答えて

3

メンバータイプを直接派生クラスに応じてにすることはできますか? auto(返される型の推定値)で宣言されたメンバー関数の結果型を取得すると、は不可能です。

ですから、ソリューションで行うよう型特性の使用が最善との唯一の解決策です。

派生クラスが定義されている場合、基本クラスは完全型でなければなりません。コンパイラは、派生クラス定義を解析する前に、まずベースクラス定義をインスタンス化して解析する必要があります。C++ standard N4140 [derived.class]/2(太字は鉱山です):

The type denoted by a base-type-specifier shall be a class type that is not an incompletely defined class;[...]

+0

あなたはこの無意味な二重否定を気づいたことがありますか? – Oliv

1

これは簡略化されていますが、いくらかの値段を払ってください。

このようなものについては何
#include <any> 

template <class T> class Base { 
    std::any id; // expensive, but cannot have T::TypeId here 
public: 
    Base() : id(123) {} 
    auto getId() { 
     return std::any_cast<typename T::TypeId>(id); 
    } // T::TypeId is OK inside a member function 
}; 

class Derived : public Base<Derived> { 
public: 
    using TypeId = int; 
}; 
+0

ありがとうございます@ n.m。あなたのソリューションは良いですが、私は現在の環境でC++ 14に限定されているため、std :: anyを使用することはできません。これはパフォーマンス重視のアプリケーションです。 – Flyer

+1

あなたは簡単な 'any'を書いたり、' boost :: any'を使うことができます(ピンチで 'void *'を使うこともできます)。どんな場合でもパフォーマンスは低下します。 –

2

template <typename T, typename TypeId> class Base 
{ 
private: 
    TypeId id; 
public: 
    Base() { id = 123; } 
    TypeId getId() {return id;} 
}; 

class Derived : public Base<Derived, int> {}; 
+0

@ killzone-kidありがとうございました。はい、それは簡単ですが、私はベースの2番目のテンプレートパラメータを含まないソリューションを見ています。実際にこれは私が今日持っているものです。私の目標は、Baseのテンプレートパラメータの数を1つに減らすことです。 – Flyer

+0

最初のparamはここでは使用されていないことがわかりましたので、元々持っていたので残しました。あなたは 'template class Base'と' class Derived:public Base {};のような使い方ができます。 –

+0

問題に集中するために、実際のコードの要点を抽出しました。派生はBaseから継承する必要があります。 – Flyer

1

クラス階層を逆にないのはなぜ?

template <class T> 
class Base : T { 
    using TypeId = typename T::TypeId; 
    TypeId id; 
public: 
    Base() { id = 123; } 
    TypeId getId() { return id; } 
}; 

struct BasicDerived { 
    using TypeId = int; 
}; 


using Derived = Base<BasicDerived>; 
+0

私は守るべきではない。派生型は、CRTPを使用して子ベースのBaseクラスにする必要があります。 – Flyer

+0

@Flyerええ、もうCRTPではありません。 Derivedは基本クラスになり、Baseは派生クラスになりました。 –

+0

私は複数の派生クラスを持っています。ベースは親クラスにとどまる必要があります。それは面白い考えでした:) – Flyer

0

正直なところ、ハード循環依存の壁に当たっています。 どのような脱出は臭いです。
2つのテンプレート引数は、最終的には小さな値段のようです。

DerivedとTypeIDをとるダミーのテンプレートクラスを宣言できますか?私はそれがあなたに何かを得るとは思わない。

Is TypeID:1:1マッピングを派生しましたか? TypeIDから派生した別のヘルパーテンプレートを使って1:1のマッピングをバックルックアップすることを過度に表現する方が良いでしょうか?これを行うには、TypeIDをDerivedクラスの外部で定義する必要があることに注意してください。
TypeIDは実際にクラス内で定義する必要がありますか?内部typedefの既存の使用をサポートするために、渡された定義をBaseでリーチできますか?

ダブルインクルードできますか?テンプレートの前に含めることができる基本クラス定義にtypeidが含まれるように、派生の定義を分割またはmacrioseしますか?このDerivedBaseは名前空間で宣言することができ、完全なDerivedクラスへのtypedefリンクを含んでいるため、Baseは参照用にそれを見つけることができます。

+0

あなたの提案をありがとう。実際、私は2番目のテンプレートパラメータを保持するかもしれません。 "TypeID:1:1マッピングを導出しましたか?"はい "TypeIDから派生したバックルックアップのために、別のヘルパーテンプレートを使用して1:1マッピングを過度に表現する方が良いでしょうか?" BaseTraitsテンプレートのような意味ですか?知りません。 2番目のテンプレートパラメータよりもはるかに単純ではありません。 "実際にクラス内でTypeIDを定義する必要がありますか?"いいえ、私はフォローアップの質問を理解していません。 「ダブルインクルードまたは分割しますか?」それは面白い。あなたはtypedefリンクで何を意味するのですか? – Flyer

0

実際には、これはあまりにも不愉快ではないと思った:
あなたはバインディング構造体を持つことができ、実際のクラスの直前に宣言されたマクロとして記述することもできます。
バインディング構造体は、実数型の列挙型と不完全な型定義を定義します。
テンプレートは、そのすべての前に定義されていますが、その依存関係を延期する型名を使用しますが、それは実際のクラスのように型ID使用されていることを実際のクラスと結合構造体にのみ依存

template <class ThatClassWrapper> 
class MyBase 
{ 
protected: 
    typedef typename ThatClassWrapper::TypeId TypeId; 
    typedef typename ThatClassWrapper::RealClass ThatClass; 
    TypeId typeIdValue; 
    TypeId GetTypeId() { return typeIdValue; } 
    std::vector<ThatClass*> storage; 
}; 

class SomeClass; 
namespace TypeIdBinding 
{ 
    struct SomeClass 
    { 
     enum TypeId 
     { 
      hello, world 
     }; 
     typedef ::SomeClass RealClass; 
    }; 
} 
class SomeClass: public MyBase<TypeIdBinding::SomeClass> 
{ 
public: 
    bool CheckValue(TypeId id) 
    { return id == typeIdValue; } 
}; 

注によってインスタンス化されますテンプレートベースで定義され、名前付きメンバーは直接表示されません。テンプレートBaseをバインディング構造体から派生させることで、それを修正することができます(そのようにコンパイルすることが確認されています)。私は実際にはC++ 11のように、別の名前空間から列挙型の名前をエクスポートまたはtypedefして、その名前を列挙型メンバーの接頭辞として使用して名前の汚染を避けることができます。

+0

この他にもありがとうございます。あなたは非常に創造的です:)私はあまりにも不愉快ではないことに同意しますが、それはもはやCRTPではなく、Derived> Base継承を失います。あなたが言ったように、私は循環依存の壁を打ったように思え、@olivは正しいと思います。 – Flyer

関連する問題