7

Rozważmy przypadek, gdy jeden musi zweryfikować typ T z innego szablonu g (może być trochę enable_if wyrażenie, na przykład) wewnątrz manekina parametru innego szablonu, tak:Niejednoznaczne szablon z SFINAE manekina parametru

template<class>  struct g { typedef void type; }; 
template<class, class> struct f {}; 
template<class T>  struct f<T, void> {};     // Case A 
template<class T>  struct f<T*, typename g<T>::type> {}; // Case B 

int main() { f<int*, void> test; } 

Tutaj, dla uproszczenia, g tak naprawdę nic nie robi. Drugi parametr w Przypadek B nie ma kontekstu, dlatego intuicyjnie można by pomyśleć, że Przypadek B jest bardziej wyspecjalizowany niż Przypadek A. Niestety, zarówno gcc, jak i clang będą narzekać, że szablon jest niejednoznaczny w powyższym przykładzie.

Jeśli parametr dummy miał zostać usunięty, kompiluje się poprawnie. W jaki sposób dodanie niezadukowanego parametru w jakiś sposób niweczy uzasadnione oczekiwanie, że T* jest bardziej wyspecjalizowany niż?

Oto szybkie sprawdzenie przy użyciu algorytmu podstawiania:

f<Q , void  > 
-> f<T*, g<Q>::type> // [failed] 

    f<Q*, g<Q>::type> 
-> f<T , void  > // [to fail or not to fail?] 
// One would assume that 2nd parameter is ignored, but guess not? 

Odpowiedz

5

Kiedy niejasność wynika, częściowe uporządkowanie szablonów służy do jego rozwiązania. Jednak to częściowe Zamawiający ustala się na szablonach, ponieważ są one przed dowolny podstawienie dzieje, a nie po (częściowe lub całkowite) zastąpienie zostało przeprowadzone - czyli to, czego oczekują, zastępując int dla T w typename g<T>::type, który wydajność typename g<int>::type, a zatem (z powodu definicji g) void.

Ustęp 14.8.2.4/2 określa sposób częściowe uporządkowanie jest ustalona (patrz this answer on SO do bardziej szczegółowej dyskusji na poniższym akapicie):

Dwa zestawy typu są używane do określenia częściowego uporządkowania. Dla każdego z włączonych szablonów istnieje pierwotny typ funkcji i transformowany typ funkcji. [Uwaga: Utworzenie transformowanego typu opisano w 14.5.6.2. -end note] Proces dedukcji wykorzystuje transformowany typ jako szablon argumentu i oryginalny typ drugiego szablonu jako szablonu parametru. Ten proces jest wykonywany dwa razy: dla każdego typu uczestniczącego w porównywaniu częściowych uporządkowania: po użyciu przekształconego szablonu-1 jako szablonu argumentu jako szablonu parametru i ponownego użycia przekształconego szablonu-2 jako szablonu argumentu i szablon-1 jako szablon parametrów.

Przed zastąpienia, nie wiedząc, co wartość T przyjmie, nie można powiedzieć (i ani kompilator może powiedzieć) czy przypadku B jest mniej lub bardziej wyspecjalizowane niż przypadku gdy A. Dlatego żadna z dwóch specjalizacji nie jest bardziej wyspecjalizowana niż druga.

Innymi słowy, pytanie nie jest czy to częściowego specjalizacji:

template<class T> struct f<T, void>; // Case A 

jest bardziej wyspecjalizowane niż ten jeden (otrzymany przez częściowego zastąpienia):

template<class T> struct f<T*, void>; // Case B 

Gdyby tak było, czego Odpowiedź brzmi oczywiście, że sprawa sprawa B jest bardziej wyspecjalizowana. Zamiast tego, jest pytanie, czy dla ewentualna T, to specjalność:

template<class T> struct f<T, void>; // Case A 

Czy bardziej wyspecjalizowane niż ten jeden:

template<class T> struct f<T*, typename g<T>::type>; // Case B 

Ponieważ nie można ustalić dla każdego T, przypadek B nie jest ani bardziej wyspecjalizowany i mniej wyspecjalizowany niż przypadek A, a gdy oba są opłacalne, pojawia się niejasność.

Jeśli zastanawiasz się, czy parametry non-wywnioskować kontekście są brane pod uwagę do częściowego uporządkowania, to jest wymienione w notatce z § 14.8.2.4/11:

W większości przypadków wszystko szablonu parametry muszą mieć wartości, aby odliczenie się udało, ale dla celów częściowych parametr zamówienia może pozostać bez wartości pod warunkiem, że nie jest używany w typach używanych do częściowego porządkowania. [Uwaga: Parametr szablonu używany w niezorientowanym kontekście jest uznawany za używany . końcem pamiętać]

+0

miałem świadomość, że substytucja nie odgrywa rolę w tym, ale byłem zdziwiony, dlaczego głównie z parametrem typu zależnego jest nadal uważane w algorytmie zamawiania częściowe (spodziewałem się, że będzie ignorowane). – Rufflewind

+0

@fzlogic: "Dlaczego" jest trochę nie konstruktywnym pytaniem, obawiam się. Ale fakt, że muszą one być brane pod uwagę, jest wyraźnie określony przez Standard (patrz cytat dodany na końcu odpowiedzi) –

+0

Cytat ze standardu był tym, czego szukałem w odniesieniu do mojego pytania "dlaczego". Na szczęście obejście było dość proste: wystarczy zmienić 'void' w ** Przypadek A **, aby był arbitralnym typem. – Rufflewind

Powiązane problemy