Używam mocno funkcji SFINAE w projekcie i nie jestem pewien, czy istnieją jakieś różnice między następującymi dwoma podejściami (innymi niż styl):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
Wynik programu jest zgodny z oczekiwaniami:
method 1
method 2
Done...
Widziałem metodę 2 częściej używaną w przepełnieniu stosu, ale wolę metodę 1.
Czy są jakieś okoliczności, w których te dwa podejścia się różnią?
Odpowiedzi:
Sugestia: preferuj metodę 2.
Obie metody działają z pojedynczymi funkcjami. Problem pojawia się, gdy masz więcej niż funkcję o tej samej sygnaturze i chcesz włączyć tylko jedną funkcję zestawu.
Załóżmy, że chcesz włączyć opcję
foo()
1, gdybar<T>()
(udawaj, że jest toconstexpr
funkcja)true
, afoo()
wersja 2, kiedybar<T>()
jestfalse
.Z
pojawia się błąd kompilacji, ponieważ masz dwuznaczność: dwie
foo()
funkcje z tym samym podpisem (domyślny parametr szablonu nie zmienia podpisu).Ale następujące rozwiązanie
działa, ponieważ SFINAE modyfikuje podpis funkcji.
Niepowiązana obserwacja: istnieje również trzecia metoda: włączanie / wyłączanie typu zwracanego (oczywiście z wyjątkiem konstruktorów klas / struktur)
Jako metoda 2 metoda 3 jest kompatybilna z wyborem alternatywnych funkcji o tej samej sygnaturze.
źródło
foo()
funkcja pozostanie dostępna po wywołaniu go z jawnym drugim parametrem szablonu (foo<double, double>();
wywołanie). A jeśli pozostaną dostępne, istnieje niejednoznaczność z inną wersją. W metodzie 2 SFINAE włącza / wyłącza drugi argument, a nie parametr domyślny. Nie można więc nazwać tego objaśnieniem parametru, ponieważ występuje błąd podstawienia, który nie pozwala na drugi parametr. Więc wersja jest niedostępna, więc nie ma dwuznacznościauto foo() -> std::enable_if_t<...>
jest często przydatny, aby uniknąć ukrywania podpisu funkcji i umożliwić użycie argumentów funkcji.Oprócz odpowiedzi max66 , innym powodem preferowania metody 2 jest to, że w metodzie 1 możesz (przypadkowo) przekazać parametr typu jawnego jako drugi argument szablonu i całkowicie pokonać mechanizm SFINAE. Może się to zdarzyć jako błąd literówki, błąd kopiowania / wklejania lub niedopatrzenie w większym mechanizmie szablonów.
Demo na żywo tutaj
źródło