Próbujesz użyć SFINAE wymusić wybór konkretnego kandydata w konkretnym przypadku (tu obecność na żądanie podmiotu foo
sobą nie argumentu wewnątrz T
lwartości). Co tak naprawdę się tutaj dzieje?
Szablon z void_t
jest dobrze zdefiniowany dla twojego struct A
, więc w momencie połączenia masz dwóch ważnych kandydatów do uzyskania rozdzielczości przeciążenia. Jeśli chcesz korzystać z SFINAE, musisz upewnić się, że dla danego połączenia dostępna jest tylko jedna przeciążenie. Aby to zrobić, powinieneś najpierw osadzić swój test w typie. W tym celu można wziąć przykład na Yakk's can_apply
facility które bezczelnie kopiować tutaj, ponieważ bardzo dobrze pasuje do Państwa potrzeb:
namespace details {
// if Z<Ts...> is invalid, false_type:
template <template<class...> class Z, class always_void, class... Ts>
struct can_apply : std::false_type {};
// if Z<Ts...> is valid, true_type:
template <template<class...> class Z, class... Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type {};
}
// alias to inject the void type where we need it for SFINAE:
template <template<class...> class Z, class... Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
template <typename T>
using has_foo_t = decltype(std::declval<T>().foo());
template <typename T>
using has_foo = can_apply<has_foo_t, T>;
teraz mamy tylko do korzystania cechę określoną powyżej w naszych definicji szablonu:
// The enable_if with the negation is needed to invalidate
// this implementation when T indeed has foo().
// This is what you were missing in your original idea.
template <typename T>
std::enable_if_t<!has_foo<T>::value> bar(T) {
std::cout << "T has no foo(void)" << std::endl;
}
template <typename T>
std::enable_if_t<has_foo<T>::value> bar(T) {
std::cout << "T has a foo(void)" << std::endl;
}
Możesz zobaczyć działający przykład na Coliru.
* Co sprawia, że jest bardziej wyspecjalizowana *? Obie wersje można wywoływać bez konwersji, dlatego nie wiadomo, do którego połączenia. – NathanOliver
Być może warto wspomnieć, że w momencie, gdy mamy do czynienia z przeciążeniem, nie dbamy już o to, co jest bardziej wyspecjalizowane. odbywa się to w innej fazie kompilacji. –
@RichardHodges Rozdzielczość przeciążenia jest dokładnie tam, gdzie myślisz o częściowym zamówieniu. –