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...
精彩评论