SFINAE区分签名和未签名

Chr*_*ica 8 c++ templates sfinae c++11

我有将不同的算术类型转换为半精度浮点类型的函数(只是uint16_t最低级别的函数),并且我使用SFINAE和整数和浮点源类型具有不同的函数std::enable_if:

template<typename T>
uint16_t to_half(typename std::enable_if<
                 std::is_floating_point<T>::value,T>::type value)
{
    //float to half conversion
}

template<typename T>
uint16_t to_half(typename std::enable_if<
                 std::is_integral<T>::value,T>::type value)
{
    //int to half conversion
}
Run Code Online (Sandbox Code Playgroud)

这些是通过显式实例化从通用模板化构造函数内部调用的:

template<typename T>
half::half(T rhs)
    : data_(detail::conversion::to_half<T>(rhs))
{
}
Run Code Online (Sandbox Code Playgroud)

编译并运行也很好.现在我尝试通过用两个函数替换第二个函数来区分有符号和无符号整数:

template<typename T>
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
                 std::is_signed<T>::value,T>::type value)
{
    //signed to half conversion
}

template<typename T>
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
                 std::is_unsigned<T>::value,T>::type value)
{
    //unsigned to half conversion
}
Run Code Online (Sandbox Code Playgroud)

但是一旦我尝试编译这个VS2010给了我

错误C2995 :: "uint16_t math::detail::conversion::to_half( std::enable_if<std::tr1::is_integral<_Ty>::value && std::tr1::is_signed<_Ty>::value, T>::type )"已定义的函数模板.

所以它似乎无法消除两个模板之间的歧义,但它显然与浮点版本的积分版本没有问题.

但是因为我不是那么多的模板魔术师,我可能只是遗漏了一些明显的东西(或者它可能实际上是有效的,只是一个VS2010的bug).那么为什么这不起作用?如何使用尽可能少的编程开销和仅限标准功能(如果可能的话)?

ild*_*arn 8

就个人而言,我会尽可能地避免使用SFINAE,因为你可以通过重载完成同样的事情:

template<typename T>
uint16_t to_half_impl(T val, std::true_type, std::true_type)
{
    // is_integral + is_signed implementation
}

template<typename T>
uint16_t to_half_impl(T val, std::true_type, std::false_type)
{
    // is_integral + is_unsigned implementation
}

template<typename T>
uint16_t to_half_impl(T val, std::false_type, std::true_type)
{
    // is_floating_point implementation
}

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, uint16_t>::type to_half(T val)
{
    return to_half_impl(val, std::is_integral<T>(), std::is_signed<T>());
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*itb 3

如果这不起作用,那么你的编译器就有错误。

如果包含表达式的两个函数定义满足一个定义规则,则两个涉及模板参数的表达式被认为是等效的...

这是这里要考虑的最重要的规则(省略“...”的细节)。您的两个模板不满足 ODR,因为它们的标记序列不同。

如果两个函数模板在同一作用域中声明、具有相同的名称、具有相同的模板参数列表,并且使用上述规则比较涉及模板参数的表达式具有等效的返回类型和参数列表,则这两个函数模板是等效的。

所以你的两个模板定义了不同的模板并且不会冲突。您现在可以检查您的模板是否“功能等效”。如果对于任何可能的模板参数集,您的enable_if表达式将始终产生相同的值。is_unsigned但由于和的情况并非如此is_signed,因此情况也并非如此。如果是这样,那么您的代码将是错误的,但不需要诊断(这实际上意味着“未定义的行为”)。