Chcę zająć się bardziej metaprogramowaniem szablonów. Wiem, że SFINAE oznacza „niepowodzenie zamiany nie jest błędem”. Ale czy ktoś może mi pokazać dobre zastosowanie SFINAE?
c++
templates
metaprogramming
sfinae
rlbond
źródło
źródło
Odpowiedzi:
Oto jeden przykład ( stąd ):
Gdy
IsClassT<int>::Yes
jest oceniane, nie można przekonwertować wartości 0,int int::*
ponieważ int nie jest klasą, więc nie może mieć wskaźnika elementu członkowskiego. Gdyby SFINAE nie istniało, wystąpiłby błąd kompilatora, coś w rodzaju „0 nie może zostać przekonwertowane na wskaźnik elementu członkowskiego dla typu int niebędącego klasą”. Zamiast tego po prostu używa...
formularza, który zwraca Two, a zatem zwraca wartość false, int nie jest typem klasy.źródło
...
, ale raczej toint C::*
, czego nigdy nie widziałem i musiałem iść spojrzeć w górę. Znalazłem odpowiedź na pytanie, co to jest i do czego może być używane tutaj: stackoverflow.com/questions/670734/ ...Lubię używać
SFINAE
do sprawdzania warunków boolowskich.To może być całkiem przydatne. Na przykład użyłem go do sprawdzenia, czy lista inicjatorów zebrana za pomocą przecinka operatora nie jest dłuższa niż ustalony rozmiar
Lista jest akceptowana tylko wtedy, gdy M jest mniejsze niż N, co oznacza, że lista inicjalizacyjna nie zawiera zbyt wielu elementów.
Składnia
char(*)[C]
oznacza: Wskaźnik do tablicy z typem elementu char i rozmiaremC
. JeśliC
jest fałszywe (tutaj 0), to otrzymujemy nieprawidłowy typchar(*)[0]
, wskaźnik do tablicy o zerowym rozmiarze: SFINAE sprawia, że szablon zostanie wówczas zignorowany.Wyrażone za pomocą
boost::enable_if
, wygląda to takW praktyce często uważam, że sprawdzanie warunków jest użyteczną umiejętnością.
źródło
M <= N ? 1 : -1
mogłoby zadziałać.int foo[0]
. Nie dziwię się, że jest obsługiwany, ponieważ pozwala na bardzo przydatną sztuczkę „struct kończąca się tablicą o długości 0” ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
W C ++ 11 testy SFINAE stały się znacznie ładniejsze. Oto kilka przykładów typowych zastosowań:
Wybierz przeciążenie funkcji w zależności od cech
Używając tak zwanego idiomu typu sink można przeprowadzić całkiem dowolne testy na typie, takie jak sprawdzenie, czy ma on element członkowski i czy ten element członkowski jest określonego typu
Oto przykład na żywo: http://ideone.com/dHhyHE Niedawno napisałem też całą sekcję o SFINAE i wysyłaniu tagów na moim blogu (bezwstydna wtyczka, ale odpowiednia) http://metaporky.blogspot.de/2014/08/ część-7-statyczna-wysyłka-function.html
Zauważ, że od C ++ 14 istnieje std :: void_t, który jest zasadniczo taki sam jak mój TypeSink tutaj.
źródło
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
w jednym miejscu, a potemTypeSinkT<decltype(&T::bar)>
w innym? Czy jest to&
koniecznestd::declval<T&>
?TypeSink
, C ++ 17 mająstd::void_t
:)Biblioteka enable_if firmy Boost oferuje ładny, czysty interfejs do korzystania z SFINAE. Jeden z moich ulubionych przykładów użycia znajduje się w bibliotece Boost.Iterator . SFINAE służy do włączania konwersji typu iteratora.
źródło
C ++ 17 prawdopodobnie zapewni ogólne metody zapytań o funkcje. Aby uzyskać szczegółowe informacje, patrz N4502 , ale jako samodzielny przykład rozważ poniższe.
Ta część jest częścią stałą, umieść ją w nagłówku.
Poniższy przykład, zaczerpnięty z N4502 , pokazuje użycie:
W porównaniu z innymi implementacjami ta jest dość prosta: wystarczy zredukowany zestaw narzędzi (
void_t
idetect
). Poza tym zgłoszono (patrz N4502 ), że jest on mierzalnie bardziej wydajny (czas kompilacji i zużycie pamięci kompilatora) niż poprzednie podejścia.Oto przykład na żywo , który obejmuje poprawki przenośności dla GCC w wersji wcześniejszej niż 5.1.
źródło
Oto kolejny (późno) SFINAE przykład, na podstawie Greg Rogers „s odpowiedź :
W ten sposób możesz sprawdzić
value
wartość, aby zobaczyć, czyT
jest to klasa, czy nie:źródło
int C::*
w Twojej odpowiedzi? Jak możeC::*
być nazwą parametru?int C::*
to typ wskaźnika doint
zmiennej składowejC
.Oto jeden dobry artykuł z SFINAE: Wprowadzenie do koncepcji SFINAE w C ++: introspekcja członka klasy w czasie kompilacji .
Podsumuj to w następujący sposób:
declval
to narzędzie, które daje „fałszywe odniesienie” do obiektu typu, którego nie można łatwo skonstruować.declval
jest bardzo przydatny w naszych konstrukcjach SFINAE.źródło
Tutaj używam przeciążenia funkcji szablonu (nie bezpośrednio SFINAE), aby określić, czy wskaźnik jest wskaźnikiem funkcji lub klasy elementu członkowskiego: ( Czy można naprawić wskaźniki funkcji elementu członkowskiego iostream cout / cerr drukowane jako 1 lub prawda? )
https://godbolt.org/z/c2NmzR
Wydruki
W istocie kod mógłby (w zależności od "dobrej" woli kompilatora) wygenerować wywołanie funkcji, która zwróci prawdę lub fałsz w czasie wykonywania. Jeśli chcesz wymusić
is_function_pointer(var)
ocenę w typie kompilacji (żadne wywołania funkcji nie są wykonywane w czasie wykonywania), możesz użyćconstexpr
zmiennej sztuczki:Standard C ++
constexpr
gwarantuje , że wszystkie zmienne zostaną ocenione w czasie kompilacji ( Obliczanie długości łańcucha w języku C w czasie kompilacji. Czy to naprawdę jest constexpr? ).źródło
Poniższy kod używa SFINAE, aby umożliwić kompilatorowi wybranie przeciążenia na podstawie tego, czy typ ma określoną metodę, czy nie:
Wynik:
źródło