Próbuję uzyskać prosty przykład do pracy, aby zrozumieć, jak używać std::enable_if
. Po przeczytaniu tej odpowiedzi pomyślałem, że nie powinno być trudno znaleźć prosty przykład. Chcę użyćstd::enable_if
do wyboru między dwiema funkcjami składowymi i pozwolić na użycie tylko jednej z nich.
Niestety, poniższe nie skompiluje się z gcc 4.7 i po wielu godzinach prób, pytam was, jaki jest mój błąd.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc zgłasza następujące problemy:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Dlaczego g ++ nie usuwa niewłaściwego wystąpienia drugiej funkcji składowej? Zgodnie ze standardem std::enable_if< bool, T = void >::type
istnieje tylko wtedy, gdy parametr szablonu logicznego ma wartość true. Ale dlaczego g ++ nie uważa tego za SFINAE? Myślę, że komunikat o przeciążeniu pochodzi z problemu, że g ++ nie usuwa drugiej funkcji składowej i uważa, że powinno to być przeciążenie.
std::is_same< T, int >::value
i! std::is_same< T, int >::value
co daje ten sam rezultat.Odpowiedzi:
SFINAE działa tylko wtedy, gdy podstawienie w dedukcji argumentu z argumentu szablonu powoduje, że konstrukcja jest źle sformułowana. Nie ma takiej zamiany.
Dzieje się tak, ponieważ kiedy tworzony jest szablon klasy (co ma miejsce, gdy tworzysz obiekt typu
Y<int>
między innymi przypadkami), tworzy on instancję wszystkich swoich deklaracji składowych (niekoniecznie ich definicji / treści!). Wśród nich są także szablony członków. Zauważ, żeT
jest to znane i!std::is_same< T, int >::value
daje fałsz. Więc utworzy klasę,Y<int>
która zawieraPlik
std::enable_if<false>::type
Dostęp non-istniejący typ, tak że deklaracja jest źle sformułowane. A zatem twój program jest nieprawidłowy.Musisz uzależnić szablony elementów członkowskich
enable_if
od parametru samego szablonu elementu członkowskiego. Wtedy deklaracje są ważne, ponieważ cały typ jest nadal zależny. Kiedy próbujesz zadzwonić do jednego z nich, następuje dedukcja argumentów dla ich argumentów szablonu i SFINAE dzieje się zgodnie z oczekiwaniami. Zobacz to pytanie i odpowiadającą mu odpowiedź, jak to zrobić.źródło
Y
tworzona jest instancja klasy szablonu, kompilator w rzeczywistości nie skompiluje funkcji składowych szablonu; jednak kompilator WYKONUJE podstawienieT
do szablonu DECLARATIONS, tak aby te szablony członkowskie mogły zostać utworzone w późniejszym czasie. Ten punkt awarii nie jest SFINAE, ponieważ SFINAE ma zastosowanie tylko podczas określania zestawu możliwych funkcji do rozwiązania przeciążenia , a tworzenie wystąpienia klasy nie jest przypadkiem określania zestawu funkcji do rozwiązania przeciążenia. (A przynajmniej tak mi się wydaje!)Zrobiłem ten krótki przykład, który również działa.
Skomentuj, jeśli chcesz, żebym to rozwinął. Myślę, że kod jest mniej więcej oczywisty, ale z drugiej strony zrobiłem to, więc mogę się mylić :)
Możesz zobaczyć to w akcji tutaj .
źródło
error C4519: default template arguments are only allowed on a class template
.Q
, mimo że jest równaT
?test
funkcji członkowskiej. Obie nie mogą istnieć w tym samym czasie.Q
po prostu przekazuje typ szablonu klasyT
. Możesz usunąć szablon klasy w następującyT
sposób: cpp.sh/4nxw, ale to trochę mija się z celem.Dla tych spóźnionych, którzy szukają rozwiązania, które „po prostu działa”:
Połącz z:
Bieganie daje:
źródło
std::enable_if_t
naresolvedType
.Z tego postu:
Ale można zrobić coś takiego:
źródło
Jednym ze sposobów rozwiązania tego problemu, specjalizacją funkcji składowych, jest umieszczenie specjalizacji w innej klasie, a następnie dziedziczenie z tej klasy. Być może trzeba będzie zmienić kolejność dziedziczenia, aby uzyskać dostęp do wszystkich innych danych bazowych, ale ta technika działa.
Wadą tej techniki jest to, że jeśli chcesz przetestować wiele różnych rzeczy dla różnych funkcji składowych, musisz utworzyć klasę dla każdej z nich i połączyć ją w drzewo dziedziczenia. Dotyczy to dostępu do wspólnych członków danych.
Dawny:
źródło
Wartość logiczna musi zależeć od wydedukowanego parametru szablonu. Tak więc prostym sposobem na naprawienie jest użycie domyślnego parametru boolowskiego:
Jednak to nie zadziała, jeśli chcesz przeciążać funkcję składową. Zamiast tego najlepiej jest używać
TICK_MEMBER_REQUIRES
z biblioteki Tick :Możesz również zaimplementować własne makra członkowskie, takie jak to (na wypadek, gdybyś nie chciał używać innej biblioteki):
źródło
Oto mój minimalistyczny przykład z użyciem makra. Użyj podwójnych nawiasów,
enable_if((...))
gdy używasz bardziej złożonych wyrażeń.źródło