Helper class works correctly, but I'm sitting here staring at it wondering what the hell I'm looking at. Could you explain this to me?
Sure do.
So first of our outer class should act as a replacement of std::is_aritmetic, so we set it up the interface like this:
template<typename T>
class acceptsType
{
public:
static const bool value = //???
};
So the value of the compile-time evaluated "value" variable should depend both on the type, as well as the value of is_aritmetic. So we do just that, by defining an inner helper class that takes two template arguments:
template<typename T2, bool isAritmetic = false>
class helper
{
public:
static const bool value = false;
};
So now we call it by making it take <T, std::is_aritmetic<T>::value>, and assigning its "value" to the outer-class "value":
static const bool value = typename helper<T, std::is_arithmetic<T>::value>::value;
And the base template "helper" class now defines that for any type where is_arithmetic is "false", "value" will also be false. But we want to support some types though, so we add a specialisation for every arbitrary type where is_arithmetic is actually true:
template<typename T2>
class helper<T2, true>
{
public:
static const bool value = true;
};
This uses partial specialisation, and translates to what I've just said - arbitary T2, and std::is_arthmetic == true will generate this overload.
So at this state adding anything except int, float, ... will return "false" and thus fail your SFINEA. But adding additional types is now really easy.
template<>
class helper<std::string, false>
{
public:
static const bool value = true;
};
This time, we use a full specialistation - we know the value of T2 (std::string), and that it is not arithmetic. This combats the ambiquity that you described in your attempts to fix it - due to the second template parameter, you always get a closes match for the types.
Hope this solves any questions, if you are still confused, I'll try to explain anything that is left over.