5

不思議なことに繰り返されるテンプレート パターンを使用すると、基本クラスから参照しようとした場合にのみ、派生クラスに属する typedef を参照できません。gcc は文句を言いno type named 'myType' in class Derived<...>ます。これは、typedef、テンプレート、および不思議なことに繰り返される関係を使用して他の方法で可能なことと矛盾しているようです。

検討:

/* crtp.cpp */

#include <iostream>
using namespace std;

// case 1. simple.

class Base {
public:
    typedef int a_t;

    a_t foo;
};

class Derived : public Base {
    a_t bar;
};

// case 2. template.

template<typename T>
class tBase {
public:
    typedef T b_t;
    T foo;
};

template <typename T>
class tDerived : public tBase<T> {
    typename tBase<T>::b_t bar;
};

// case 3. curiously recurring.

template <typename T, typename D>
class tCuriousBase {
public:
    typedef T c_t;
    c_t foo;
};

template <typename T>
class tCuriousDerived : public tCuriousBase<T,tCuriousDerived<T> > {
    typename tCuriousBase<T,tCuriousDerived<T> >::c_t bar;
};

// case 4. curiously recurring with member reference.

template <typename T, typename D>
class tCuriousMemberBase {
public:
    T foo;

    T get() {
        return static_cast<D*>(this)->bar;
    }
};

template <typename T>
class tCuriousMemberDerived : public tCuriousMemberBase<T, tCuriousMemberDerived<T> > {
public:
    T bar;

    tCuriousMemberDerived(T val) : bar(val) {}
};

// case 5. curiously recurring with typedef reference.

template <typename T, typename D>
class tCuriousTypeBase {
public:
    typedef T d_t;
    d_t foo;
    typename D::c_t baz;
};

template <typename T>
class tCuriousTypeDerived : public tCuriousTypeBase<T, tCuriousTypeDerived<T> > {
public:
    typedef T c_t;
    typename tCuriousTypeBase<T,tCuriousTypeDerived<T> >::d_t bar;
};

// entry point

int main(int argc, char **argv) {
    Derived::a_t one = 1;
    tDerived<double>::b_t two = 2;
    tCuriousDerived<double>::c_t three = 3;
    double four = tCuriousMemberDerived<double>(4).get();
    tCuriousTypeBase<double, tCuriousDerived<double> >::d_t five = 5;
    // tCuriousTypeDerived<double>::d_t six = 6; /* FAILS */

    cout << one   << endl;
    cout << two   << endl; 
    cout << three << endl;
    cout << four  << endl;
    cout << five  << endl;
    // cout << six << endl;
}

(1) から、typedef は実際にベースから派生に継承されていることがわかります。基本クラスで宣言された typedef は、派生クラスを介してアクセスできます。

(2) から、両方のクラスがテンプレートである場合でも、これは真であることがわかります。

(3) から、この typedef 継承は、不思議なことに繰り返されるテンプレート関係の存在下でも存在できることがわかります。の宣言で、派生クラスを介してベースの typedef を参照しますthree

(4) から、派生クラスのメンバー変数は基底クラスから容易にアクセスできることがわかります。

(5) から、テンプレート パラメーターで定義された typedef にアクセスできることがわかります (これはfive、継承によって関連付けられていない型を使用して宣言するときに機能します)。

ただし、(6) でテンプレート パラメーターを派生クラスにするとすぐに、派生クラスのメンバー変数と同じように適切に定義されているように見えても、突然 typedef にアクセスできなくなります。

どうしてこれなの?

4

1 に答える 1

9

これが「循環依存症」、つまり「不完全型」の分身です。

テンプレート「メタプログラミング」は「プログラミングタイプ」ですが、タイプを適切にインスタンス化するには、特定のレベルのセマティクスを知る必要があります。

このアナロジーを考えてみましょう:

class A; //forwarded

class B
{
   A* pa; //good: we know how wide a pointer is, no matter if we don't know yet anything about A.
   A a; // bad: we don't know how much space it requires
};

class A
{
  float m;
}; // now we know, but it's too late

これは、A を B の前に配置することで解決できますが、A が

class A
{
   B m;
};

A 再帰は無限になるため、ポインター以外の解決策はありません。(A はそれ自体を含む必要があり、別のコピーを参照しないでください)

さて、同じ例えで、「型」をプログラムしましょう:

template<class D>
class A
{
   typedef typename D::inner_type my_type; //required D to be known when A<D> is instantiated...
   my_type m; // ... otherwise we cannot know how wide A<D> will be.
};

D を次のように定義し始めるまでは、この宣言自体は悪くありません。

class D: //here we only know D exist
    public A<D> //but A size depende on D definition...
{
  ....
  typedef long double; inner_type
  ....
}; // ....we know only starting from here

したがって、基本的に、D を作成するために A を使用する必要がある時点で、A の幅は (まだ) わかりません。

この「循環性」を破る 1 つの方法は、CRT ループの外側でいくつかの「特性クラス」を使用することです。

struct traits
{
   typedef long double inner_type;
   ....
};

template<class D, class Traits>
class A
{
  // this is easy: Traits does not depend itself on A
  typedef typename Traits::inner_type my_type;
  ....
};

template<class Traits>
class D: public A<D, Traits>
{
  typedef typename Traits::inner_type inner_type;
};

ついに宣言できる

typedef D<traits> D_Inst;

A::my_typeつまり、との間の一貫性は、定義が独立しているD::inner_typeによって保証されます。traits::inner_type

于 2013-04-19T07:59:32.230 に答える