To answer your question.comment to Kerre's answer, you could try to use SFINAE:
#include <type_traits>
#include <string>
template <class T>
struct HasFooImpl_ {
template <typename C>
static std::true_type test(decltype(fooImpl(std::declval<C>()))*);
template <typename C>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
};
template <typename T>
using HasFooImpl = typename HasFooImpl_<T>::type;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}
You'd have to implement a function fooImpl
for any type that you don't want to be handled genericly.
The implementation was a bit tricky, I tried just enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value
first, but for the fallback the !is_same<>::value
gave me compiler errors, because it tried to instantiate the decltype as well.
This implementation has one caveat that you might or might not want to use: if T
is convertible to some other type that has a fooImpl
defined, that conversion will kick in.
You can see the whole thing in action here: http://ideone.com/3Tjtvj
Update: if you don't want to allow type conversions, it actually gets easier:
#include <type_traits>
#include <string>
template <typename T> void fooImpl(T);
template <typename T>
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}