2

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.

user673679
  • 1,327
  • 1
  • 16
  • 35
  • Little confused why you didn't include `IsFloatingPoint` in this post. – WhozCraig Apr 15 '16 at 22:07
  • @WhozCraig because now there's twice as much code, and most of it's irrelevant. – user673679 Apr 15 '16 at 22:09
  • The concept of SFINAE doesn't make sense for variable templates, since they can be neither overloaded nor partially specialized. – Brian Bi Apr 15 '16 at 22:11
  • 1
    @user673679 Why not use `std::is_floating_point::value` ? – Alejandro Apr 15 '16 at 22:11
  • 1
    @Brian "nor partially specialized" {{citation needed}} – T.C. Apr 15 '16 at 22:15
  • @user673679 I don't have access to MSVC, does [this](http://coliru.stacked-crooked.com/a/dbbff8fd5a465607) compile on it? – Alejandro Apr 15 '16 at 22:15
  • @Alejandro No it doesn't, `constexpr not valid here` is the error on vs2015 upd1 (but clang happily eats it up). – WhozCraig Apr 15 '16 at 22:18
  • @Alejandro because I didn't know it existed. ;) And yep, that does compile on vs2015 update 2. – user673679 Apr 15 '16 at 22:20
  • I'm tempted to make that an answer, but I'm afraid that it may violate some standards rule that I'm unaware of. Maybe @T.C. can confirm? – Alejandro Apr 15 '16 at 22:25
  • @Alejandro If you'd like to make that an answer (and maybe explain the second enable_if_t template argument a little) I'll happily accept it. – user673679 Apr 16 '16 at 18:15
  • @Alejandro Looks OK to me, though I don't see much point in doing it this way. Might as well make it call a constexpr function template and do a `static_assert` with a nice message inside. – T.C. Apr 18 '16 at 10:27

1 Answers1

4

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};
}
Alejandro
  • 3,040
  • 1
  • 21
  • 30