В общем случае может быть 2-3 подхода перечисленных ниже. В базе
всегда SFINAE -- шаблон откидывается и просматриваются следующие,
если тип в шаблоне не может быть выведен. Сегодня, при
использовании C++17, можно использовать идиому void_t (см. на
cppreference.com) для того, чтобы написать шаблон типа
раскрывающийся по-разному (true/false) если для его параметра можно
или нельзя определить возвращаемое значение искомой функции (через
decltype) которая как будто присутствует (через declval) и далее с помощью enable_if выбрать шаблон.
В C++14 нет void_t но его можно самому определить (как -- описано на cppreference.com). В C++11 нет std::declval ("как будто есть тип"), но он заменяется на static_cast<T*>(0) во всех почти случаях.
Вместо того, чтоб расписывать enable_if(одно) и enable_if(!одно-а-другое), можно ввести понятие "приоритета" шаблонной функции и выбирать их по приоритету, через функцию-диспетчер. Как -- показано в моём примере, который для C++11. Там же избавились от declval, но остались с decltype, который принципиально необходим. Кстати там же показано, что от enable_if иногда можно избавиться. Ведь компилятору нужен не enable_if, а лишь факт (не)выводимости типа.
В C++03 ситуация намного хуже, так как там нет decltype. Это принципиальная проблема, и приходится выкручиваться через sizeof(), который работает так же, но возвращает не тип, а размер. Ну а размер может оказаться разный, в зависимости от того как раскроется шаблон структуры применённый к типу. В итоге информацию о типе извлечь можно, но только в виде числа, а не типа, и так закодировано может быть ограниченное множество типов. Но для выбора да/нет вполне годится.
#include <stdio.h>
struct A
{
void f() { puts("f"); }
};
struct B : public A
{
void super_f() { puts("super_f"); }
};
#if 0 // C++17 (C++14 if you write own std::void_t), in C++11 you also need to write own std::declval
#include <type_traits>
#include <utility>
#if 0// variant which requires std::void_t
template <typename, typename = void> struct HasSuperF : std::false_type {};
template <typename T> struct HasSuperF<T, std::void_t<decltype(std::declval<T>().super_f())> > : std::true_type {};
template <typename T>
typename std::enable_if<! HasSuperF<T>::value, void>::type test(T v)
{
v.f();
}
template <typename T>
typename std::enable_if<HasSuperF<T>::value, void>::type test(T v)
{
v.super_f();
}
#else// other C++11 variant
template <int Order> struct Priority : Priority<Order - 1> {};
template <> struct Priority<0> {};
template <typename T>
void _test(T v, Priority<0>)
{
v.f();
}
template <typename T>
decltype(void(static_cast<T*>(0)->super_f())) _test(T v, Priority<1>)
{
v.super_f();
}
template <typename T>
void test(T v) { _test(v, Priority<1>()); }
#endif
#else// C++03
template <class Class>
struct HasSuperF
{
private:
template <typename T, T> struct equal_types;
template <typename> static int check(...);
template <typename C> static char check(equal_types<void (C::*)() const, &C::super_f> *);
template <typename C> static char check(equal_types<void (C::*)(), &C::super_f> *);
public:
static bool const value = sizeof(check<Class>(0)) == sizeof(char);
};
template <bool, typename> struct EnableIf;
template <typename T> struct EnableIf<true, T> { typedef T type; };
template <class T>
typename EnableIf<! HasSuperF<T>::value, void>::type test(T v)
{
v.f();
}
template <class T>
typename EnableIf<HasSuperF<T>::value, void>::type test(T v)
{
v.super_f();
}
#endif
int main()
{
test(A());
test(B());
return 0;
}
http://coliru.stacked-crooked.com/a/99da17f3635144bc