2016-03-28 7 views
2

Warum verursacht der folgende Code einen Fehler? Ich würde denken, dass der Compiler hier einfach die entsprechende Überladung auswählt?Fehler mit SFINAE

#include <iostream> 
using std::cout; 
using std::endl; 

template <typename ToCheckFor> 
struct InterfaceCheck { 

    // used by the constexpr function, the function will pass in a pointer to 
    // a type with the required types 
    template <typename _ToCheckFor, void (_ToCheckFor::*)()> 
    struct InterfaceCheckImplTag {}; 

    // used to check for the presence of a function print() 
    // template <typename T> 
    // static constexpr bool function(__attribute__((unused)) void* ptr) {} 
    template <typename T> 
    static constexpr bool function(__attribute__((unused)) void* ptr) { 
     return false; 
    } 
    template <typename T> 
    static constexpr bool function (__attribute__((unused)) 
     InterfaceCheckImplTag<T, &T::print>* ptr) { 

     return true; 
    } 

    constexpr static const bool value = function<ToCheckFor>(nullptr); 
}; 

struct Something { 
    void print() { cout << "Something::print()" << endl; } 
}; 

int main() { 

    cout << InterfaceCheck<Something>::value << endl; 

    return 0; 
} 

Warum ersetzt das void* Argument mit einem Auslassungs den Code funktioniert wie erwartet? So funktioniert der folgende Code als

erwartet
#include <iostream> 
using std::cout; 
using std::endl; 

template <typename ToCheckFor> 
struct InterfaceCheck { 

    // used by the constexpr function, the function will pass in a pointer to 
    // a type with the required types 
    template <typename _ToCheckFor, void (_ToCheckFor::*)()> 
    struct InterfaceCheckImplTag {}; 

    // used to check for the presence of a function print() 
    // template <typename T> 
    // static constexpr bool function(__attribute__((unused)) void* ptr) {} 
    template <typename T> 
    static constexpr bool function(...) { 
     return false; 
    } 
    template <typename T> 
    static constexpr bool function (__attribute__((unused)) 
     InterfaceCheckImplTag<T, &T::print>* ptr) { 

     return true; 
    } 

    constexpr static const bool value = function<ToCheckFor>(nullptr); 
}; 

struct Something { 
    void print() { cout << "Something::print()" << endl; } 
}; 

int main() { 

    cout << InterfaceCheck<Something>::value << endl; 

    return 0; 
} 

Antwort

2

Warum wird der folgende Code einen Fehler verursachen?

Es gibt zwei mögliche Alternativen für die function Überladungen. Beide beinhalten eine Umwandlung von dem mitgelieferten Argumente, und weder Umwandlung ist besser als die andere:

error: call to 'function' is ambiguous 
    constexpr static const bool value = function<ToCheckFor>(nullptr); 
             ^~~~~~~~~~~~~~~~~~~~ 
test.cpp:36:13: note: in instantiation of template class 'InterfaceCheck<Something>' requested here 
    cout << InterfaceCheck<Something>::value << endl; 
      ^
test.cpp:17:27: note: candidate function [with T = Something] 
    static constexpr bool function(__attribute__((unused)) void* ptr) { 
         ^
test.cpp:21:27: note: candidate function [with T = Something] 
    static constexpr bool function (__attribute__((unused)) 

Befestigung mit function(...) funktioniert, weil die Umwandlung von etwas ... ist immer ein „schlechtes“ Spiel als alles anderes (aber immer noch legal). Es ist ein wunderbarer Trick, wenn du es erst einmal kennst.

Von 13.3.3.2 Rang impliziter Konvertierung Sequenzen [over.ics.rank]:

  1. Wenn die Grundformen der impliziten Umwandlung Sequenzen zu vergleichen (wie in 13.3 definiert .3.1)

    • eine Standardkonvertierungssequenz (13.3.3.1.1) ist eine bessere Umwandlungsfolge als eine benutzerdefinierte Konvertierungssequenz oder eine Auslassungs Umwandlungsfolge und

    • 012.351.
    • Eine benutzerdefinierte Konvertierungssequenz (13.3.3.1.2) ist eine bessere Konvertierungssequenz als eine Ellipsenumwandlungssequenz (13.3.3.1.3).

Geschichte

Zuerst lernte ich diese Technik aus Modern C++ Design, Abschnitt 2.7. Ich weiß nicht genau, ob das hier erfunden wurde. Aber das ist keine schlechte Schätzung. Das Buch ist jetzt 15 Jahre alt und es ist immer noch eine gute Lektüre.

+0

Schön! Gibt es noch etwas über "..." das ich wissen sollte? Wie wo es benutzt wird? – Curious

+1

Ich benutze es nie außer in Meta-Programmen wie dem in Ihrer Frage. Es kann zur Laufzeit verwendet werden. Ich habe keinen Nutzen dafür. Ich werde stattdessen variadische Vorlagen verwenden (was auch die Ellipsis-Syntax beinhaltet, aber nicht dasselbe ist). –