SFINAE differentiation between signed and unsigned

二次信任 提交于 2019-11-30 19:25:37

If this doesn't work then your compiler is at error.

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule ...

That's the most important rule to consider here (left out the details of "..."). Your two templates do not satisfy the ODR because their token sequences differ.

Two function templates are equivalent if they are declared in the same scope, have the same name, have identical template parameter lists, and have return types and parameter lists that are equivalent using the rules described above to compare expressions involving template parameters.

So your two templates define different templates and do not clash. You could now check whether your templates perhaps are "functionally equivalent". They would be if for any possible set of template arguments, your enable_if expression would always yield the same value. But since that is not true for is_unsigned and is_signed, this is not the case either. If it would, then your code would be ill-formed, but without requiring a diagnostic (which effectively means "undefined behavior").

Personally, I would avoid SFINAE here as much as possible since you can accomplish the same thing with overloading:

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

The more common idiom is to use SFINAE on the return type rather than the argument type. Otherwise, the template type T may not be deducible. With

// from C++14
template<bool C, typename T> using enable_if_t = typename std::enable_if<C,T>::type;

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

template<typename T>
enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value,  int16_t>
to_half(T value)
{
    //unsigned to half conversion
}

the type T in the following statement

auto y=to_half(x);    // T is deduced from argument, no need for <T> 

is deducible (even trivially), but for your original code it is not! Indeed, when running this statement with your to_half() implementation through clang gives

test.cc:24:11: error: no matching function for call to 'to_half'
  auto x= to_half(4);
          ^~~~~~~
test.cc:7:10: note: candidate template ignored: couldn't infer template argument 'T'
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
         ^
test.cc:15:10: note: candidate template ignored: couldn't infer template argument 'T'
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
         ^

Of course, if one explicitly provides the template argument (as you did), this problem does not appear. So your code wasn't wrong (but the compiler), but what's the point of SFINAE if you pass the template argument type?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!