First, I think SFINAE should usually be hidden from interfaces. It makes the interface messy. Put the SFINAE away from the surface, and use tag dispatching to pick an overload.
Second, I even hide SFINAE from the traits class. Writing "can I do X" code is common enough in my experience that I don't want to have to write messy SFINAE code to do it. So instead I write a generic can_apply
trait, and have a trait that SFINAE fails if passed the wrong types using decltype
.
We then feed the SFIANE failing decltype
trait to can_apply
, and get out a true/false type depending on if the application fails.
This reduces the work per "can I do X" trait to a minimal amount, and places the somewhat tricky and fragile SFINAE code away from day-to-day work.
I use C++1z's void_t
. Implementing it yourself is easy (at the bottom of this answer).
A metafunction similar to can_apply
is being proposed for standardization in C++1z, but it isn't as stable as void_t
is, so I'm not using it.
First, a details
namespace to hide the implementation of can_apply
from being found by accident:
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, std::void_t<Z<Ts...>>, Ts...>:
std::true_type{};
}
We can then write can_apply
in terms of details::can_apply
, and it has a nicer interface (it doesn't require the extra void
being passed):
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
The above is generic helper metaprogramming code. Once we have it in place, we can write a can_to_string
traits class very cleanly:
template<class T>
using to_string_t = decltype( std::to_string( std::declval<T>() ) );
template<class T>
using can_to_string = can_apply< to_string_t, T >;
and we have a trait can_to_string<T>
that is true iff we can to_string
a T
.
The work require to write a new trait like that is now 2-4 lines of simple code -- just make a decltype
using
alias, and then do a can_apply
test on it.
Once we have that, we use tag dispatching to the proper implementation:
template<typename T>
std::string stringify(T t, std::true_type /*can to string*/){
return std::to_string(t);
}
template<typename T>
std::string stringify(T t, std::false_type /*cannot to string*/){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<typename T>
std::string stringify(T t){
return stringify(t, can_to_string<T>{});
}
All of the ugly code is hiding in the details
namespace.
If you need a void_t
, use this:
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
which works in most major C++11 compilers.
Note that the simpler template<class...>using void_t=void;
fails to work in some older C++11 compilers (there was an ambiguity in the standard).