2011-09-05 32 views
6

我用我的类作为它的父类中的一个模板参数,而父类使用它的模板参数(不过的sizeof ())。使用子类的基类的模板参数,并为嵌套名指定

,编译器给我:

错误:不完全类型“祈求:: workerClass {又名MyClass的}”嵌套名指定中使用

然而,类文件中被明确定义。我想这是因为子类没有在基类的实例化的时刻实例化,但那种事情发生的CRTP而且也没有问题。

我用的是子​​类的模板参数的原因是为了做一个不同的函数调用如果子类有,还是没有,具体的功能。

下面是测试的最小码

/* Structure similar to boost's enable if, to use 
    SFINAE */ 
template <int X=0, class U = void> 
struct test { 
    typedef U type; 
}; 

enum Commands { 
    Swim, 
    Fly 
}; 

/* Structure used for template overloading, 
    as no partial function template specialization available */ 
template<Commands T> 
struct Param { 

}; 

template <class T> 
class Invoker 
{ 
public: 
    typedef T workerClass; 

    workerClass *wc() { 
     return static_cast<workerClass*>(this); 
    } 

    template <Commands command> 
    void invoke() { 
     invoke2(Param<command>()); 
    } 

    /* If the child class has those functions, call them */ 
    /* Needs template paramter Y to apply SFINAE */ 
    template<class Y=int> 
    typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type 
    invoke2(Param<Fly>) { 
     wc()->fly(); 
    } 

    template<class Y=int> 
    typename test<sizeof(Y)+sizeof(decltype(&workerClass::swim))>::type 
    invoke2(Param<Swim>) { 
     wc()->shoot(); 
    } 

    template<Commands command> 
    void invoke2(Param<command>) { 
     /* Default action */ 
     printf("Default handler for command %d\n", command); 
    } 
}; 

template <class T, class Inv = Invoker<T> > 
class BaseClass : public Inv 
{ 
public: 
    template<Commands command> 
    void invoke() { 
     Inv::template invoke<command>(); 
    } 
}; 

class MyClass : public BaseClass<MyClass> 
{ 
public: 
    void swim() { 
     printf("Swimming like a fish!\n"); 
    } 

    /* void fly(); */ 
}; 


void testing() { 
    MyClass foo; 
    foo.invoke<Fly>(); /* No 'void fly()' in MyClass, calls the default handler */ 
    foo.invoke<Swim>(); /* Should print the swimming message */ 
} 

错误发生在该行:

typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type 

那么,有没有支持此的编译器,或者是这明确地由标准的规定无效使用模板?我是否必须改变我这样做的方式并找到解决办法? CRTP让我希望代码可能是有效的,但我不确定。

如果这真的是不可能的,那么到底为什么?为什么CRTP工作?

+4

好了,问题很清楚,至少:你正在做一个自我指涉的递归定义:'BaseClass的'需要,因为'decltype的完整'MyClass' '表达式,但是'MyClass'需要一个完整的'BaseClass '作为基类。 –

+1

您需要将特化隐藏在额外的间接层后面,以便它们不作为基类定义的一部分进行评估。把它们放在一个私有的嵌套类型中,并且都可以正常工作。 – ildjarn

+0

正是由于这个原因,CRTP很棘手,所面临的问题很容易理解:基类模板作为类声明的一部分被实例化,但尚未完成(认为类型依赖于什么基本模板可能会添加,所以编译器无法知道派生类型会是什么样的,而无需首先处理基本模板 - 考虑到基本可以添加更改大小的成员属性,或者可以更改基本模板的虚拟成员函数函数声明在派生类型中的含义) –

回答

2

的解决方案是,作为ildjarn指出,要增加一个间接另一个层次。

即通过改变测试函数接受类型进行:

template <typename X, class U = void> 
struct test { 
    typedef U type; 
}; 

,然后再通过子类为模板参数,而不是从指定它一开始走:

template<class Y=workerClass> 
    typename test<decltype(&Y::fly)>::type 
    invoke2(Param<Fly>) { 
     wc()->fly(); 
    } 

    template<class Y=workerClass> 
    typename test<decltype(&Y::swim)>::type 
    invoke2(Param<Swim>) { 
     wc()->swim(); 
    } 

这样,嵌套说明符只在函数被调用时才被评估,而不是在类评估中评估,到那时候子类已经被评估。另外可以传递默认的模板参数,我们可以调用没有任何模板参数的函数。

的模板也更可读了。和示例代码现在工作得很好:

class MyClass : public BaseClass<MyClass> 
{ 
public: 
    void swim() { 
     printf("Swimming like a fish!\n"); 
    } 

    /* void fly(); */ 
}; 


void testing() { 
    MyClass foo; 
    foo.invoke<Fly>(); /* No 'void fly()' in MyClass, calls the default handler */ 
    foo.invoke<Swim>(); /* Should print the swimming message */ 
} 
+0

这非常有趣。任何人都可以为此提供一个有趣的用例吗? – Zhro

+0

@Zhro我使用它的方式是制作一个像虚拟功能一样的系统,而不会产生由虚拟表引起的(可忽略的)开销。 – coyotte508

相关问题