使用带有多个模板参数的CRTP时如何声明模板默认值?

时间:2011-09-21 14:08:44

标签: c++ templates c++11 crtp

我想这样做:

template <class Derived=BattleData>
class BattleData : public BattleCommandManager<Derived> {
};

但显然没有声明BattleData,所以我试了一个前瞻声明:

template <class T> class BattleData;

template <class Derived=BattleData>
class BattleData : public BattleCommandManager<Derived> {
};

但后来我

  

错误:“第二行上的模板参数数量错误,有   BattleData。

我真的没有看到解决方法!

修改

我这样做的原因是因为我希望能够直接使用BattleData作为class,但我也希望能够将其子类化,在这种情况下我必须指定派生的class作为第二个template参数。

例如,假设我的BattleData类的语料库是:

template <class Derived> class BattleData: public BaseClass<Derived> {
    void foo1(){};
    void foo2(){};
    void foo3(){};
}

我有一个子类

template class SubBattleData: public BattleData<SubBattleData> {
    void foo1(){};
}

在某些情况下,我仍然希望能够编写如下代码:

BattleData *x = new BattleData(...);

如果不能使用默认参数,我甚至无法执行以下操作:

BattleData<BattleData> *x = new BattleData<BattleData>(...);

一方面,在BattleData类中没有虚拟化函数的原因是没有虚函数的好处。它对我不起作用的另一个原因是,其中一个父CRTP类只有在派生类型中存在函数时才会调用函数(使用decltype(Derived::function)和enable-if like结构),然后回退到默认值否则就是行为由于可能存在大量具有特定设计模式的函数(如CRTP,它读取具有许多不同情况的协议,并且仅当派生类指定相应的函数时才以特定方式处理案例,否则只需传输它而不进行处理)。

因此,这些函数可以出现在SubBattleData而不是BattleData中,但是如果实例化这两个类都可以正常工作,但实例化BattleData是不可能的。

4 个答案:

答案 0 :(得分:3)

您应该能够比上述更自然地完成原始设计目标。您不能清楚地使用实际的Derived typename作为默认值,因为您真正想要编写的内容如下:

template <class Derived=BattleData <BattleData <BattleData <...>>>
class BattleData : public BattleCommandManager<Derived> {
};

你明白了。相反,只需使用像void一样的占位符:

template <typename T = void>
class BattleData : public BattleCommandManager <
    typename std::conditional <
        std::is_same <T, void>::value, 
        BattleData <void>,
        T
    >::type>
{
};

免责声明:我没有编译上述内容。

答案 1 :(得分:1)

我看不到你想要做什么。

有什么问题
template <class T=DataContainer>
class BattleData : public BattleCommandManager< BattleData<T> > {
};

如果指定Derived不是实际的派生类,那么静态多态无法工作,无论如何CRTP都会变得无用。

编辑:从我收集的内容来看,这就是你想要的抽象术语:

template <class Derived> 
struct Base {
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

template<typename T>
struct Derived : Base<Derived> {
  // dummy so we get you example
  T t;
  void implementation() {
    std::cout << "derived" << std::endl;
  }
};

struct Derived2 : public Derived<int> {
  // hide implementation in Derived
  // but still have Base::interface make the right call statically
  void implementation() {
    std::cout << "derived2" << std::endl;
  }
};

我无法知道你可以做到这一点。另一个 方法是使用策略类而不是CRTP。他们是 与继承兼容,你可以实现类似的行为。

template<typename Policy>
struct BattleCmdManager : public Policy {
  using Policy::foo;
};

template<typename T>
struct BattleData {
  // ...
protected:
  void foo();
};

struct BattleData2 : public BattleData<int {
  // ...
protected:
  void foo();
};

答案 2 :(得分:1)

你不能为第二个模板参数使用Empty类吗?

template <class T=DataContainer, class Derived=BattleData<T, Empty> >
class BattleData : public BattleCommandManager<Derived> {
};

答案 3 :(得分:0)

以下是我解决它的方法:

template <class Derived> class BattleDataInh: public BaseClass<Derived> {
    void foo1(){};
    void foo2(){};
    void foo3(){};
};

template class SubBattleData: public BattleDataInh<SubBattleData> {
    void foo1(){};
};

class BattleData : public BattleDataInh<BattleData> {
};

这样,我也可以添加任何其他模板参数。解决方案一直在我眼前,但我没有看到它......