2016-05-06 10 views
0

Ich versuche, ein paar templatisierte Funktionen für Substitution Fail Is Not An Error(SFINAE) zu verwenden. Und ich kann es so machen:Warum können diese templatisierten Funktionen keine Argumente annehmen?

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*); 
template<typename R> static false_type Test(...); 

Aber ich verstehe nicht, wie das Argument diesen SNFIAE funktioniert. Es scheint, als ob ich nur in der Lage sein sollte, um die Argumente zu entfernen und die Template-Auswahl würde genau die gleiche Art und Weise funktionieren:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(); 
template<typename R> static false_type Test(); 

Aber es funktioniert nicht, erhalte ich:

Aufruf überladenen ‚Tests () 'ist mehrdeutig

Worum geht es bei diesen Argumenten, die diese SFINAE funktionieren lassen?

Antwort

3

Ihr zweites Beispiel kann nicht kompiliert werden, da es zwei Überladungen von Test mit identischer Signatur gibt, da default-type Argumente nicht Teil der Funktionssignatur sind. Das ist nicht erlaubt.

Ihr erstes Beispiel arbeitet in followign Weise:

Wenn Typ R eine Funktion test in es hat, beide Test werden gültig Überlastung Kandidaten. Ellipsenfunktionen haben jedoch einen niedrigeren Rang als Nicht-Ellipsen-Einsen, und daher wählt der Compiler die erste Überladung aus und gibt true_type zurück.

Wenn R test nicht enthält, wird die erste Überladung von der eingestellten Überladungsauflösung ausgeschlossen (SFINAE funktioniert). Ihnen bleibt nur der zweite, der zurückgibt.

+0

Aber darüber hinaus ist es etwas Besonderes, diese Argumentation Typen, die sie verursacht in der richtigen Reihenfolge zu bewerten. Ich verstehe nicht, warum der Compiler "R *" zu "..." vorzieht. Kannst du das erklären? –

+0

@ JonathanMee, wird zur Antwort hinzufügen. – SergeyA

+0

@ JonathanMee um es einfach zu sagen, (...) ist die am wenigsten bevorzugte Übereinstimmung mit einer Argumentliste bei der Durchführung der Überladungsauflösung. Dies ist beabsichtigt und im Standard vorgeschrieben. Zufälligerweise ist es auch der am wenigsten bevorzugte Weg, eine Funktion mit variablen Argumenten zu implementieren :-) –

1

Die Frage wurde beantwortet, aber vielleicht ist es sinnvoll, eine tiefere Erklärung zu geben.

Hoffentlich kommentierte Programm macht die Dinge klarer:

#include <utility> 
#include <iostream> 

// define the template function Test<R> if and only if the expression 
// std::declval<R>().test() 
// is a valid expression. 
// in which case Test<R, decltype(std::declval<R>().test())>(0) will be preferrable to... (see after) 
template<typename R, typename S = decltype(std::declval<R>().test())> 
    static std::true_type Test(R*); 

// ...the template function Test<R>(...) 
// because any function overload with specific arguments is preferred to this 
template<typename R> static std::false_type Test(...); 


struct foo 
{ 
    void test(); 
}; 

struct bar 
{ 
    // no test() method 
// void test(); 
}; 


// has_test<T> deduces the type that would have been returned from Test<T ... with possibly defaulted args here>(0) 
// The actual Test<T>(0) will be the best candidate available 
// For foo, it's Test<foo, decltype(std::declval<R>().test())>(foo*) 
// which deduces to 
// Test<foo, void>(foo*) 
// because that's a better match than Test<foo>(...) 
// 
// for bar it's Test<bar>(...) 
// because Test<bar, /*error deducing type*/>(bar*) 
// is discarded as a candidate, due to SFNAE 
// 
template<class T> 
constexpr bool has_test = decltype(Test<T>(0))::value; 


int main() 
{ 
    std::cout << has_test<foo> << std::endl; 
    std::cout << has_test<bar> << std::endl; 
}