Can I use enable_if (or is there some alternative technique available) with template variables. e.g.
typedef float Float;
typedef double Double;
template<class T>
constexpr Bool IsFloat = std::is_same_v<T, Float>;
template<class T>
constexpr Bool IsDouble = std::is_same_v<T, Double>;
template<class T>
constexpr Bool IsFloatingPoint = IsFloat<T> || IsDouble<T>;
template<class T>
using EnableIfFloatingPoint = std::enable_if_t<IsFloatingPoint<T>>;
template
<
class T,
typename = EnableIfFloatingPoint<T>
>
constexpr T Pi = T(3.1415926535897932384626433832795);
Visual Studio gives me a compiler error saying "too few template arguments" when I try to use Pi<float>
, for example.
Right off the bat I'd recommend using std::is_floating_point
instead of hand-rolling your own floating-point detection mechanism. Additionally, you'll be able to use the _v
suffix instead of ::value
as of C++17.
As was mentioned in some of the comments, using SFINAE itself on variable templates doesn't make much sense, but you could achieve your solution of only permitting floating point types to be able to take on the value of Pi
by attempting to set the variable template to the type std::enable_if_t< std::is_floating_point<T>::value, T>
, which will of course only have a deduced type if the condition is satisfied.
template<class T>
using EnableIfFloatingPoint = std::enable_if_t<std::is_floating_point<T>::value, T>;
template<class T>
constexpr T Pi = EnableIfFloatingPoint<T>(3.1415926535897932384626433832795);
Pi<T>
for integral types T
will simply fail to compile, because
EnableIfFloatingPoint<T>
won't have any type deduced, but I wouldn't consider this SFINAE.
A better solution would be to have a constexpr
function template with an appropriate static_assert
message that verified that the template was instantiated with the "correct" type.
template<class T>
constexpr T Pi()
{
using is_fp = std::is_floating_point<T>;
static_assert( is_fp::value, "Pi must be instantiated with floating point types.");
return T{3.1415926535897932384626433832795};
}