变量模板和 std::enable_if

variable templates and std::enable_if

我可以对模板变量使用 enable_if(或者是否有其他可用的替代技术)。例如

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);
例如,当我尝试使用 Pi<float> 时,

Visual Studio 给我一个编译器错误提示 "too few template arguments"。

我建议立即使用 std::is_floating_point 而不是手动滚动您自己的浮点检测机制。此外,从 C++17 开始,您将能够使用 _v 后缀而不是 ::value

正如一些评论中提到的,在变量模板上使用 SFINAE 本身没有多大意义,但您可以实现仅允许浮点类型能够采用 [= 的值的解决方案15=] 通过尝试将变量模板设置为类型 std::enable_if_t< std::is_floating_point<T>::value, T>,如果满足条件,它当然只有推导类型。

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> 对于整数类型 T 将无法编译,因为 EnableIfFloatingPoint<T> 不会推导出任何类型,但我不会考虑这个 SFINAE。

更好的解决方案是使用一个 constexpr 函数模板,其中包含一条适当的 static_assert 消息,用于验证该模板是使用 "correct" 类型实例化的。

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