我正在使用我的类作为其父类之一的模板参数,并且该父类在模板参数中使用它(尽管sizeof())。
编译器给了我:
错误:在嵌套名称说明符中使用的不完整类型'Invoker :: workerClass {aka 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能够正常工作呢?
答案 0 :(得分:2)
正如ildjarn指出的那样,解决方案是添加另一个间接层。
通过将测试函数更改为接受类型来完成:
template <typename X, class U = void>
struct test {
typedef U type;
};
然后将子类作为模板参数传递,而不是从get go指定它:
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 */
}