You can use std::is_signed together with std::enable_if:
template<typename T>
T diff(T a, T b);
template<typename T>
std::enable_if_t<std::is_signed<T>::value, T> diff(T a, T b) {
return a - b;
}
Here std::is_signed<T>::value is true if and only if T is signed (BTW, it is also true for floating-point types, if you don’t need it, consider combining with std::is_integral).
std::enable_if_t<Test, Type> is the same as std::enable_if<Test, Type>::type. std::enable_if<Test, Type> is defined as an empty struct in case Test is false and as a struct with an only typedef type equal to template parameter Type otherwise.
So, for signed types, std::enable_if_t<std::is_signed<T>::value, T> is equal to T, while for unsigned it’s not defined and compiler uses SFINAE rule, so, if you need to specify an implementation for a particular non-signed type, you can easily do that:
template<>
unsigned diff(unsigned, unsigned)
{
return 0u;
}
Some relevant links: enable_if, is_signed.