c++enable-ifvariable-templates

variable templates and std::enable_if


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.


Solution

  • 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};
    }