开发者

How to declare a template default value when using CRTP with several template parameters?

I want to do:

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

But obviously BattleData isn't declared, so I tried a forward declaration:

template <class T> class BattleData;

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

But then I get

error: "wrong number of template parameter on the second line, with BattleData.

I really fail to see a solution to this!

Edit:

The reason I'm doing this is because I want to be able to use BattleData directly as a class, but I also want to be able to subclass it in 开发者_开发百科which case I have to specify the derived class as the second template parameter.

For example let's say the corpus of my BattleData class is :

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

And I have a subclass

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

I would still want, in some cases, to be able to write code like this:

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

I can't even do the following without being able to use default arguments:

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

On one side, the reason functions aren't virtualized in the BattleData class is the benefit of having no virtual function. The other reason it doesn't work for me is that one of the parent CRTP classes invokes functions only if they're present in the derived type (using decltype(Derived::function) and enable-if like structures), and fall back to default behavior otherwise. Since there can be a great deal of those functions with a particular design pattern (like a CRTP that reads a protocol with many different cases and processes a case a particular way only if the derived class specify the corresponding function, otherwise just transfer it without processing).

So those functions can be present in SubBattleData and not BattleData, but both classes would work fine if instantiated, yet it's impossible to instantiate BattleData.


You should be able to accomplish your original design goals more naturally than the above. You can't use the actual Derived typename as the default clearly because what you're really trying to write is the following:

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

You get the idea. Instead, just use a placeholder like void:

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

Disclaimer: I did not compile the above.


Can't you use an Empty class for the second template parameter?

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


I don't see what you are trying to do. What is wrong with

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

If you specify Derived to be something else than the actual derived class static polymorphism is not going to work and CRTP becomes somewhat useless anyway.

Edit: From what I have gathered this is what you want to in abstract terms:

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;
  }
};

There is no way I know of that you can make this work. Another approach would be to use policy classes instead of CRTP. They are compatible with inheritance and you can achieve similar behaviour.

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();
};


Here is how I solved it:

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> {
};

And that way, I can add any other template parameters too. The solution was in front of my eyes the whole time but I didn't see it...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜