SFINAE有符号和无符号之间的区别
SFINAE differentiation between signed and unsigned
我有将不同算术类型转换为半精度浮点类型的函数(只有最低级别的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
}
这些是通过显式实例化从通用模板构造函数内部调用的:
template<typename T>
half::half(T rhs)
: data_(detail::conversion::to_half<T>(rhs))
{
}
这编译起来也很好。现在,我试图通过用以下两个函数替换第二个函数来区分有符号整数和无符号整数:
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
}
但一旦我试图编译这个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错误)。那么,为什么这不起作用,以及如何在尽可能少的编程开销和仅限标准功能的限制下(如果可能的话)使其起作用呢?
就我个人而言,我会尽可能避免在这里使用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>());
}
如果这不起作用,则编译器出错。
如果包含表达式的两个函数定义满足一个定义规则,则包含模板参数的两个表达式被认为是等价的。。。
这是这里要考虑的最重要的规则(省略了"…"的细节)。您的两个模板不满足ODR,因为它们的令牌序列不同。
如果两个函数模板在同一范围内声明、具有相同名称、具有相同的模板参数列表,并且具有等效的返回类型和参数列表(使用上述规则来比较涉及模板参数的表达式),则它们是等效的。
因此,您的两个模板定义了不同的模板,并且不会发生冲突。您现在可以检查您的模板是否"功能等效"。如果对于任何可能的模板参数集,enable_if
表达式总是产生相同的值,则会出现这种情况。但由于is_unsigned
和is_signed
的情况并非如此,因此情况也并非如此。如果是这样,那么您的代码将是格式错误的,但不需要诊断(这实际上意味着"未定义的行为")。
更常见的习惯用法是对返回类型而不是参数类型使用SFINAE否则,模板类型T
可能无法推导。带
// 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
}
以下语句中的类型T
auto y=to_half(x); // T is deduced from argument, no need for <T>
是可推导的(即使很琐碎),但对于您的原始代码来说,它不是!事实上,当通过clang在to_half()
实现中运行此语句时,会给出
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 &&
^
当然,如果显式地提供了模板参数(正如您所做的那样),则不会出现此问题。所以你的代码没有错(但编译器错了),但如果你传递模板参数类型,SFINAE有什么意义?
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 这 4 个 lambda 表达式之间有什么区别?
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- (double) 和 double() 之间的区别
- & 和 * 之间的区别
- std::is_convertible 和 std::convertible_to 之间的区别(在实践中)?
- 析构函数和'delete'之间的区别
- 在 typedef 内部使用 const 关键字和在 typedef 外部使用 const 关键字之间有区别吗?
- 从预序遍历构造 bst 的 c++ 和 python 解决方案之间的区别
- vector.back() 和 vector[vector.size() - 1] 之间的区别?
- 空指针常量 (nullptr)、空指针值和空成员指针值之间有什么区别?
- 函数签名与调用的函数不匹配,常量字符[]和字符*之间的区别?
- OpenMP #pragma omp for v/s #pragma omp parallel for 之间的区别?
- S() 与 S{} 之间的区别?