繁体   English   中英

使用子类作为基类的模板参数和嵌套名称说明符

[英]Using child class as a template parameter of a base class and as a nested name specifier

我正在使用我的类作为其父类之一的模板参数,并且该父类在模板参数中使用它(尽管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有效呢?

正如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 */
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM