"Inverse SFINAE" 避免模棱两可的过载
"Inverse SFINAE" to avoid ambiguous overload
如果第二个模板实例化,如何防止下面的第一个模板实例化? (即如果同时定义了 static_cast<T>(0)
和 T::zero()
)
template<typename T>
auto zero() ->decltype(static_cast<T>(0)) {
return static_cast<T>(0);
}
template<typename T>
auto zero() ->decltype(T::zero()) {
return T::zero();
}
没有enable_if
,依赖整数转换行列的内置规则(0
->int
的转换优于0
->char
, 这使得前者成为第一选择候选人,而后者成为可行的第二选择候选人):
template <typename T>
auto zero_helper(char) -> decltype(static_cast<T>(0))
{
return static_cast<T>(0);
}
template <typename T>
auto zero_helper(int) -> decltype(T::zero())
{
return T::zero();
}
template <typename T>
auto zero() -> decltype(auto)
{
return zero_helper<T>(0);
}
使用您自己的 enable_if
谓词(类似于 std::void_t
技术):
#include <type_traits>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename T, typename = void_t<>>
struct has_zero : std::false_type {};
template <typename T>
struct has_zero<T, void_t<decltype(T::zero())>> : std::true_type {};
template <typename T>
auto zero()
-> typename std::enable_if<has_zero<T>::value, decltype(T::zero())>::type
{
return T::zero();
}
template <typename T>
auto zero()
-> typename std::enable_if<!has_zero<T>::value, T>::type
{
return static_cast<T>(0);
}
如果您需要通过对重载等级的细粒度控制将其扩展到多个重载,一种常用的技术是使用标签调度。
template<int r>
struct rank : rank<r - 1> {};
template<>
struct rank<0> {};
template<typename T>
auto zero_impl(rank<0>) -> decltype(static_cast<T>(0)) {
return static_cast<T>(0);
}
template<typename T>
auto zero_impl(rank<1>) ->decltype(T::zero()) {
return T::zero();
}
template<typename T>
auto zero() { return zero_impl<T>(rank<10>{}); }
派生到碱基的转换将优先选择最接近的碱基 class。这转化为调用具有最高级别的重载。因为在编译器的眼中,那个将具有最好的隐式转换序列。
如果第二个模板实例化,如何防止下面的第一个模板实例化? (即如果同时定义了 static_cast<T>(0)
和 T::zero()
)
template<typename T>
auto zero() ->decltype(static_cast<T>(0)) {
return static_cast<T>(0);
}
template<typename T>
auto zero() ->decltype(T::zero()) {
return T::zero();
}
没有enable_if
,依赖整数转换行列的内置规则(0
->int
的转换优于0
->char
, 这使得前者成为第一选择候选人,而后者成为可行的第二选择候选人):
template <typename T>
auto zero_helper(char) -> decltype(static_cast<T>(0))
{
return static_cast<T>(0);
}
template <typename T>
auto zero_helper(int) -> decltype(T::zero())
{
return T::zero();
}
template <typename T>
auto zero() -> decltype(auto)
{
return zero_helper<T>(0);
}
使用您自己的 enable_if
谓词(类似于 std::void_t
技术):
#include <type_traits>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename T, typename = void_t<>>
struct has_zero : std::false_type {};
template <typename T>
struct has_zero<T, void_t<decltype(T::zero())>> : std::true_type {};
template <typename T>
auto zero()
-> typename std::enable_if<has_zero<T>::value, decltype(T::zero())>::type
{
return T::zero();
}
template <typename T>
auto zero()
-> typename std::enable_if<!has_zero<T>::value, T>::type
{
return static_cast<T>(0);
}
如果您需要通过对重载等级的细粒度控制将其扩展到多个重载,一种常用的技术是使用标签调度。
template<int r>
struct rank : rank<r - 1> {};
template<>
struct rank<0> {};
template<typename T>
auto zero_impl(rank<0>) -> decltype(static_cast<T>(0)) {
return static_cast<T>(0);
}
template<typename T>
auto zero_impl(rank<1>) ->decltype(T::zero()) {
return T::zero();
}
template<typename T>
auto zero() { return zero_impl<T>(rank<10>{}); }
派生到碱基的转换将优先选择最接近的碱基 class。这转化为调用具有最高级别的重载。因为在编译器的眼中,那个将具有最好的隐式转换序列。