问题
How can I prevent the first template below from instantiating if the second template instantiates? (i.e. if both static_cast<T>(0)
and T::zero()
are defined)
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();
}
回答1:
If you need to extend it to multiple overloads with fine grained control of overload rank, a common technique is to use tag dispatching.
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>{}); }
Derived to base conversions will prefer the closest base class. Which translates to calling the overload with the highest rank. Since that one will have the best implicit conversion sequence in the eyes of the compiler.
回答2:
Without enable_if
, relying on built-in rules of integer conversion ranks (conversion of 0
-> int
is better than 0
-> char
, which makes the former a first choice candidate, and the latter a viable second choice candidate):
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);
}
DEMO
With your own enable_if
predicate (similar to the std::void_t
technique):
#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);
}
DEMO 2
来源:https://stackoverflow.com/questions/60247980/inverse-sfinae-to-avoid-ambiguous-overload