2017-06-26 11 views
0

基本CRTPクラスのリーフCRTPクラスのネストされたクラスを使用できるかどうかを理解したいと思います。以下の例は、この問題を示しています。CRTP - ネストされたリーフクラスのタイプの可視性

#include <iostream> 

using namespace std; 

template<class T> 
class A 
{ 

protected: 
    T* asLeaf(void) 
     {return static_cast<T*>(this);} 
    T const* asLeaf(void) const 
     {return static_cast<T const*>(this);} 
public: 

    struct Inner 
    {int a = 10;}; 

    void dispInner(void) const 
     {std::cout << asLeaf()->inner.a << std::endl;} 

    // I would like to use T::Inner in this class, e.g. 
    // typename T::Inner mvInnerA; 
    // However, I understand that it is not possible to 
    // use it in the form that is stated above. Thus, 
    // I am looking for any possible workarounds. 

}; 


class B: public A<B> 
{ 
public: 
    struct Inner: public A<B>::Inner 
    {int b = 20;}; 

protected: 
    friend A<B>; 
    B::Inner inner; 

public: 
    void dispInner(void) const 
     { 
      A<B>::dispInner(); 
      std::cout << asLeaf()->inner.b << std::endl; 
     } 
}; 


int main() 
{ 

    B b; 
    b.dispInner(); 

    return 0; 

} 

EDIT

私が受け取ったフィードバックに基づいて、いくつかの更なるコメントを提供したいと思います:

  • 私は、適切な設計手法を使用していないことを承知しています。特に、Ainnerの存在を意識しているべきかどうかが疑問視されるかもしれません。しかし、innerのタイプをB::Innerと定義し、innerの定義をBに指定し、Aに使用する代わりにAに定義したいと思います。
  • 私はBおよび/またはB::Innerと宣言することができず、これを行うことができない理由については知らせています。したがって、技術的には、設計上の問題には解決策がありません。しかし、私は実行可能な回避策を探しています。可能な実現可能な解決策の一つはAB::Inner inner「を定義」とその機能を提供するAのメンバ関数を使用する試みを作ることではありません

    • :私はすでにいくつかの代替ソリューションを検討している

    A<B>::Innerの部分をB::Inner innerに変更することができます。

  • もう1つの解決策は、クラスA<B>::InnerB::Innerを明示的に(すなわちネストされたクラスとしてではなく)定義することです。しかし、私は、設計によって、Aから派生していない任意のクラスがA<B>::Innerと対話する必要があることを期待するかしないので、これを避けることを好むだろうA<B>::Inner

どちらのソリューションから派生するクラスそのI提示されたものが受け入れられるかもしれない。しかし、私は実現可能な選択肢を探しています。

+0

一般的なルールは、あなたのBaseクラスがその子について知ってはならないということです.CRC(https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)を使用しているのはなぜですか? – lapinozz

+1

@lapinozzです。それには何も間違っていません。 –

+0

@lapinozz - 動的多型の考え方は、CRTPの静的多型に完全に直交しています。同じ「ルール」が必ずしも適用されるわけではありません。 – StoryTeller

答えて

1

標準があると言う:

クラスは、クラス指定の閉鎖}で完全に定義されたオブジェクトのタイプ(または完全なタイプ)であると考えられます。

あなたがA<B>としてAを専門とするときBが完全に定義されたオブジェクトではないということになります。そのため、Aの定義内のメンバーや型などにアクセスすることはできません(メンバーメソッドAの定義内から派生クラスをコールバックすることはできますが、 CRTPイディオムの目的よりも)。他の点では
、あなたが行う場合は、この:

typename T::Inner mvInnerA 

あなたはTが完全に定義されたオブジェクトであるという保証を持っていないし、あなたがエラーを取得する理由です。


いくつかの選択肢:

  1. あなたは関数として代わりのタイプとしてmvInnerTypeを定義し、タイプT::innerのオブジェクトを作成するためのファクトリとしてそれを使用することができます。

    [static] auto mvInnerA() { 
        return typename T::Inner{}; 
    } 
    

    をそれを次のように使用してください:

    auto foo = A<B>::mvInnerA(); 
    

    または:

    auto foo = obj.mvInnerA(); 
    

    右のフォームは、あなたがそれstaticか作るという事実に依存します。あなたはまだその名前はアクセスできない場合でも、何とか隠しタイプを使用することができます
    注:

    template<typename U = T> 
    using mvInnerA = typename U::Inner; 
    

    using HiddenType = decltype(A<B>::mvInnerA()); 
    HiddenType foo = A<B>::mvInnerA(); 
    
  2. エイリアス宣言このようなテンプレートを使用してmvInnerAを定義することができます次にとしてそれを使用する:Tがあるタイプで

    auto foo = A<B>::mvInnerA<>{}; 
    

    を(私が言わせて) 0 U によって間接的に使用される場合は、mvInnerAがインスタンス化された場合のみ、上記の問題は発生しません。その代価は厄介な<>の存在と、mvInnerAにカスタムタイプを渡すことができるという事実です。

+0

あなたの答えをありがとう。残念ながら、私はエラーが発生する理由を知っています(私の謝罪を受け入れてください、私は質問の定義を修正します)。しかし、私は回避策を探しています。 – user1391279

+0

オハイオ州、オハイオ州、質問から、あなたはそれがそのまま動作すると思われるようです。私は、あなたが代替的なアプローチを求めていることを理解していませんでした。ごめんなさい。 – skypjack

+0

@ user1391279答えを編集する前に、[this](https://wandbox.org/permlink/2JcbrV65osH9BuFW)のような解決策が得られますか?言い換えれば、 'mvInnerA'はもはや型ではなく、戻り値の型が' T :: inner'である関数です。その関数の意味で型のインスタンスを作成したい場合や作成する場合はいつでも、 'decltype'を使ってそれを得ることができます。 – skypjack

0

内部タイプのCRTPテンプレートパラメータを使用する方法は厳しく制限されています。

クラステンプレート定義自体の範囲内で使用することはできません。テンプレートをインスタンス化するときは、型Bを完全に定義する必要があります。skypjack points outのように、そうではありません。ただし、クラステンプレートで即座にインスタンス化されないコンテキストで使用することができます。これは主にAのメンバー関数です。

そして、あなたはB::Innerの型の別名を持つことができない一方で、あなたはタイプエイリアステンプレートAのメンバ関数は、typename B::Innerの冗長性を回避し、代わりにInner<B>を使用するために使用することができます

template<class C> 
using Inner = typename C::Inner 

を持つことができます。

+0

最後のコメントとあなたの答えをありがとう。残念ながら、コメントと定義の定義ですでに述べたように、私はメンバ変数 'B :: Inner inner'を' A'で定義することができず、なぜこれを行うことができないのか分かりません。しかし、私は自分自身をまだ考えていない 'A ::'の 'B :: Inner'を使うことを可能にする実現可能な回避策があるかどうかを理解したかったのです。残念ながら、私は問題の適切な声明を出すことに失敗したことを認めなければなりません。 – user1391279

関連する問題