Your code fails to compile because in the "has typedef" case, both overloads exist and are valid. The compiler cannot pick between them. There are 3 basic approaches.
First, simply invert the condition. You write "has typedef" and "does not have typedef" in a sfinae friendly way, and enable one function if has, and the other if has not.
I find this is often ugly and scales poorly.
A second approach is to make it so when both are valid, the one you want is preferred, by using overload resolution order tricks (inheritance, varargs, conversion, whatever). This scales slightly better, but I find it hacky and a bit brittle.
A third approach is tag dispatching. This does not support "there is no valid overload" well, but is otherwise clean.
You write a base function. This function does some type-computations and builds some tag types with the result. You then call your implementation functions with the tags, and use that to dispatch.
Imagine you have the trait has_foo<T>
. It is type that inherits from std::true_type
if the type T
has the property foo
, and false_type
otherwise.
Then we can:
void bar_impl(std::true_type);
void bar_impl(std::false_type);
template<class T>
void bar(T const&){
bar_impl( has_foo<T>{} );
}
and the bar_impl
corresponding to if T
has the property foo
is called.
Writing such a test is easy. Here is a little library:
namespace details{
template<template<class...>class Z, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, decltype(void(std::declval<Z<Ts...>>())), Ts...>:
std::true_type
{};
}
template<template<class...>class Z,class...Ts>
using can_apply=typename details::can_apply<Z,void,Ts...>::type;
Then we just write our test:
template<class T>
using bob_subtype = typename T::bob;
Is the type T::bob
. We can test if something has a bob:
template<class T>
using has_bob=can_apply<bob_subtype,T>;
And done.