coy*_*508 6 c++ templates c++11
我正在使用我的类作为其父类之一的模板参数,并且该父类在模板参数中使用它(尽管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 */
}
Run Code Online (Sandbox Code Playgroud)
错误发生在该行:
typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type
Run Code Online (Sandbox Code Playgroud)
那么,是否有任何编译器支持这一点,或者标准明确指定为无效使用模板?我是否必须改变我这样做的方式并找到解决办法?CRTP给了我希望代码可能有效,但我不确定.
如果这真的不可能,那么为什么确切地说,为什么CRTP有效呢?
正如 ildjarn 指出的那样,解决方案是添加另一个间接级别。
这是通过更改测试函数以接受类型来完成的:
template <typename X, class U = void>
struct test {
typedef U type;
};
Run Code Online (Sandbox Code Playgroud)
然后将子类作为模板参数传递,而不是从一开始就指定它:
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();
}
Run Code Online (Sandbox Code Playgroud)
这样,嵌套说明符仅在调用函数时而不是在类求值时求值,并且此时子类已经求值。另外,由于可以传递默认模板参数,我们可以在没有任何模板参数的情况下调用该函数。
该模板现在也更具可读性。示例代码现在工作得很好:
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 */
}
Run Code Online (Sandbox Code Playgroud)