Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specialize template for type derived from particular type

Tags:

c++

templates

I have class World which manages creation of object... After creation it calls afterCreation method and I the created object is user-defined type derived from Entity (eg. MyEntity), I want to call addEntity. I the object was something else, I want to do nothing. addEntity must be called with appropriate T, because it generates unique IDs for every derived class etc.

Here is my solution:

template <int v>
struct ToType
{
    enum { value = v };
};

template <typename T>
void World::afterCreation(T * t)
{
    afterCreation(t, ToType<std::is_base_of<Entity, T>::value>());
}

template <typename T>
void World::afterCreation(T * t, ToType<true>)
{
    addEntity(t); //here I cant pass Entity *, I need the real type, eg. MyEntity
}

template <typename T>
void World::afterCreation(T * t, ToType<false>)
{

}

My question is - Can in be done better way?

How can I simulate following code without ToType or similar?

template <typename T>
void afterCreation(){/*generic impl*/}

template <typename T where T is derived from Entity>
void afterCreation(){/*some specific stuff*/}
  • "specialize" in the title is only to describe my intention, no need to solve problem with template specialization
like image 357
relaxxx Avatar asked Oct 09 '22 06:10

relaxxx


2 Answers

It is not going to make it much better, but you can remove one level of indirection by using SFINAE:

template <typename T>
typename std::enable_if< std::is_base_of<Entity, T>::value >::type
 World::afterCreation(T * t)
{
   // Derived from Entity
}
template <typename T>
typename std::enable_if< !std::is_base_of<Entity, T>::value >::type
 World::afterCreation(T * t)
{
   // generic
}

How does this work? When the compiler finds the call to afterCreation it tries to determine which of the overloads is best, and for that it matches the types and tries to perform the substitution. In both cases, the matched type (from the arguments) and apply the substitution to the whole expression. The enable_if template contains an inner type type if the value passed as the first argument is true or else it does not contain such type. During the substitution of the types, one of the overloads will yield an invalid function signature (the one for which the condition is false) and will be dropped from the set of candidates.

like image 54
David Rodríguez - dribeas Avatar answered Oct 12 '22 21:10

David Rodríguez - dribeas


You can do this with polymorphic pointers:

template <typename T>
void afterCreation(T* x) {
    T* entity = dynamic_cast<Entity*> x;

    if (!entity) {
        // ... generic implementation
    } else {
        // ... entity implementation, use "entity"
    }
}

Though this might not be the best solution since this has a (tiny) run-time overhead. A very smart compiler might remove this overhead through static analysis but I doubt compilers will pick this up.

like image 35
orlp Avatar answered Oct 12 '22 19:10

orlp