trb*_*abb 5 c++ gcc templates typedef crtp
当使用奇怪的重复模板模式时,只有当我尝试从基类引用它们时,我才能引用属于派生类的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;
}
Run Code Online (Sandbox Code Playgroud)
从(1)开始,我们看到typedef确实从base继承到派生; 可以通过派生类访问基类中声明的typedef.
从(2),我们看到如果两个类都是模板,这仍然是正确的.
从(3)开始,我们看到这种typedef继承仍然存在于奇怪的重复模板关系中; 我们通过声明中的派生类来引用base的typedef three.
从(4)开始,我们看到派生类的成员变量可以从基类中轻松访问.
从(5),我们看到我们可以访问在模板参数上定义的typedef(当我们声明five使用与继承无关的类型时,这是有效的).
然而,只要我们将模板参数设置为(6)中的派生类,就会突然使typedef变得不可访问,即使它看起来像派生类中的任何成员变量一样定义良好.
为什么是这样?
这是"循环依赖",或"不完全类型"的改变.
模板"元编程"是"编程类型",但它需要知道某种级别的语义才能正确地实例化类型.
考虑一下这个比喻:
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
Run Code Online (Sandbox Code Playgroud)
这可以通过在B之前放置A来解决,但是如果A是
class A
{
B m;
};
Run Code Online (Sandbox Code Playgroud)
除了指针之外没有其他解决方案,因为递归将是无限的.(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.
};
Run Code Online (Sandbox Code Playgroud)
在我们开始将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
Run Code Online (Sandbox Code Playgroud)
所以,基本上,我们还不知道(当时)A有多宽需要用它来创建D.
打破这种"循环"的一种方法是在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;
};
Run Code Online (Sandbox Code Playgroud)
我们终于可以宣布了
typedef D<traits> D_Inst;
Run Code Online (Sandbox Code Playgroud)
换句话说,之间的相干性A::my_type和D::inner_type通过保证traits::inner_type,其定义是独立的.