Merkwürdiges Konstrukt endeckt :)



  • Hi an alle. Ich hab neulich mal wieder ein bißchen source code gelesen und bin dabei auf das hier gestossen:

    template <typename Y> no class_p_test (...);
          template <typename Y> yes class_p_test (void (Y::*) ());
    
          template <typename X>
          struct class_p
          {
            static bool const r = sizeof (class_p_test<X> (0)) == sizeof (yes);
          };
    

    Kann mir das mal jemand erklären bitte. Steh völlig aufm Schlauch...
    Vorallem das hier:

    template <typename Y> no class_p_test (...);
          template <typename Y> yes class_p_test (void (Y::*) ());
    

    Danke schonmal 😃



  • Das Ding sagt, ob der Typ ein Zeiger auf eine void() Memberfunktion ist. Wenn das X ein void(T::*)() ist, wird sizeof auf die Funktion aufgerufen, die 'yes' zurück gibt. Ansonsten auf 'no'. Deshalb die Ellipsen-Funktion.

    Lässt sich dann so verwenden:

    std::cout << class_p<void(*)()>::r << std::endl;
    std::cout << class_p<void(T::*)()>::r << std::endl;
    

    Im Prinzip eine einfache Form von Metaprogrammierung 😉



  • addicted_to_c schrieb:

    template <typename Y> no class_p_test (...);
          template <typename Y> yes class_p_test (void (Y::*) ());
    
          template <typename X>
          struct class_p
          {
            static bool const r = sizeof (class_p_test<X> (0)) == sizeof (yes);
          };
    

    Kann mir das mal jemand erklären bitte. Steh völlig aufm Schlauch...

    In C++ gibt es im Zusammenhang mit Templates "SFINAE" (substitution failure is not an error). Wenn der Compiler für einen Typparameter (hier Y) einen speziellen Typ einsetzt und es dabei im direkten Umfeld eine "substitution failure" gibt, dann wird dies nicht als Fehler gewertet, sondern das Template einfach ignoriert. Wenn dabei natürlich keine aufrufbare Funktion übrig bleibt, gibt es einen Fehler. Funktionen können aber überladen werden.

    Für einige Belegungen des Typparameters Y wird Y::* einen Sinn ergeben (für Klassentypen) und für andere nicht (zB für int). In Abhängigkeit von Y wird also entweder nur das erste Funktionstemplate oder alle beide in der Überladungsmenge sein. Und da die Ellipse bei der Überladungsauflösung als schlechtester Match eingestuft wird, wird also in Abhängigkeit von Y entweder die erste oder die zweite Funktion aufgerufen. Und welche Funktion aufgerufen werden würde, erfährt man über den Rückgabetyp.

    class_p ist also eine Metafunktion, die sagt, ob der übergebene Typparameter X ein Klassentyp ist oder nicht. Für Klassentypen kommt nämlich das zweite class_p_test in Frage, welches ein yes zurückgibt. Für nicht-Klassentypen kommt das erste class_p_test in Frage, welches ein no zurückgibt.

    ...
    
    struct classtype {};
    typedef int scalartype;
    
    int main()
    {
      assert(  class_p<classtype >::r );
      assert( !class_p<scalartype>::r );
    }
    


  • Hachja, da hab ich wohl ungenau gelesen, krümelkackers Erklärung stimmt natürlich.


Anmelden zum Antworten