2012-03-30 2 views
5

У меня есть класс World, который управляет созданием объекта ... После создания он вызывает метод afterCreation, и я созданный объект является определяемым пользователем типом, производным от Entity (например, MyEntity), Я хочу позвонить addEntity. Я объект был чем-то другим, я ничего не хочу делать. addEntity должна вызываться с соответствующим T, поскольку он создает уникальные идентификаторы для каждого производного класса и т.д.Как специализировать шаблон для типа, производного от определенного типа

Вот мое решение:

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>) 
{ 

} 

Мой вопрос - может быть сделано в лучший путь?

Как смоделировать следующий код без ToType или аналогичный?

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

template <typename T where T is derived from Entity> 
void afterCreation(){/*some specific stuff*/} 
  • «специализироваться» в названии только, чтобы описать мое намерение, нет необходимости, чтобы решить проблему с специализации шаблона

ответ

3

Это не собирается сделать это намного лучше, но вы можете удалить один уровень косвенности с помощью 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 
} 

Как это работает? Когда компилятор находит вызов afterCreation, он пытается определить, какая из перегрузок наилучшая, и для этого она соответствует типам и пытается выполнить подстановку. В обоих случаях сопоставляемый тип (из аргументов) и применяется подстановка ко всему выражению. Шаблон enable_if содержит внутренний тип type, если значение, переданное в качестве первого аргумента, равно true, либо оно не содержит такого типа. Во время подстановки типов одна из перегрузок приведет к недопустимой сигнатуре функции (той, для которой условие ложно) и будет удалено из набора кандидатов.

1

Вы можете сделать это с помощью полиморфных указателей:

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

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

Хотя это может быть не лучшее решение, так как у этого есть (крошечные) накладные расходы во время выполнения. Очень умный компилятор может удалить эти накладные расходы через статический анализ, но я сомневаюсь, что компиляторы подберут это.

+0

Эдди Эдвардс: просто передав указатель объекта, над которым вы хотите его запустить. 'Entity x; afterCreation (&x); '. – orlp

+0

+1, просто и точно :) Решает актуальную проблему, а не то, что задает вопрос (с гораздо более простым подходом, чем SFINAE - я предполагаю, что ничего в функции не будет компилироваться только для' Entity', т. Е. Нет ничего похожего на 'entity-> getEntityName()', который не сможет скомпилировать для типов, не связанных с Entity) –

+0

Извините ночной скребком Я отменил свои комментарии (и они применили ваш неотредактированный ответ) Я думаю, что много компилятора удалит dynamic_cast, так как он полностью знает тип x. Или, по крайней мере, столько знаний, сколько необходимо, если предположить, что шаблонное решение может работать вообще. –