Zeiger auf Member mit Traits nachweisen geht nicht



  • Hallo,

    Ich habe heute etwas mit Type Traits herumgespielt. Ich kam auf folgenden Code der "laut Angaben" true bzw false ergeben soll, je nachdem ob der uebergebene _Typ_ einem Zeiger-auf-einen-Membertyp-einer-Klasse-Typ darstellt oder nicht.

    Das wollte ich nun nachtesten, nur leider bekomme ich jedesmal 'false' heraus. Nun bin ich mir nicht mehr ganz sicher zu wissen was eigentlich in C++ mit "Zeiger auf Member"-Typ gemeint ist. Vllt kennt sich jemand damit aus und kann mir helfen. Ich haette gerne einen Fall in dem "true" herauskommt und einen anderen in dem ein "false" resultiert. Kann mir da jemand helfen?

    #include <iostream>
    
    template< typename T >
    class TypeTraits
    {
    private:
      // Pointer to Member: returns at compile time true if T is a pointer to member type
      template< class U >
      struct Ptr2MemTraits
      {
        enum { result = false };
      };
    
      template< class U, class V >
      struct Ptr2MemTraits< U V::* >
      {
        enum { result = true };
      };
    
    public:
      enum { isPointer2Member = Ptr2MemTraits< T >::result };
    };
    
    class SomeClass
    {
    private:
      typedef struct { int value; }
        Member_t;
    
    public:
      SomeClass(){}
    
      typedef Member_t*
        ptr1_t;
    
      typedef int*
        ptr2_t;
    };
    
    /*
      main
    //*/
    int main()
    {
      using namespace std;
    
      // init
      cout << "init\n" << endl;
    
      // FIXME: gibt 'false' aus, sollte 'true' ausgeben, oder nicht?!
      cout << "SomeClass::ptr1_t - pointer to member type (true)?\t " << (TypeTraits< SomeClass::ptr1_t >::isPointer2Member ? "\"true\"" : "\"false\"") << endl;
      cout << endl;
    
      cout << "SomeClass::ptr2_t - pointer to member type (false)?\t " << (TypeTraits< SomeClass::ptr2_t >::isPointer2Member ? "\"true\"" : "\"false\"") << endl;
      cout << endl;
    
      return 0;
    }
    


  • Hi,

    nur mal ganz allgemein: Bei intensiver und ausgefuchster template-Nutzung scheiden sich die Geister der Compiler ... deswegen ist die Angabe, welchen Du nutzt, essentiell.

    (auf den anderen Thread werde ich nicht antworten, weil "push-en" so unhöflich ist, dass ich keine Lust mehr habe)

    Gruß,

    Simon2.



  • SomeClass::ptr2_t ist doch kein pointer-to-member, sondern lediglich ein Zeiger-auf-SomeClass::Member_t. Dass das, was da bezeigt wird, eine innere Klasse ist, tut dabei garnichts zur Sache.

    Ein Pointer-to-Member ist die Adresse einer Membervariablen ohne dazugehöriges Objekt. Also folgendes:

    class SomeClass
    {
        int a;
    };
    
    &SomeClass::a // pointer auf member a
    


  • Simon2 schrieb:

    (auf den anderen Thread werde ich nicht antworten, weil "push-en" so unhöflich ist, dass ich keine Lust mehr habe)

    Gruß,

    Simon2.

    @Simon2
    Hm ok, sorry Simon2, war nicht meine Absicht gegen die Netiquette zu verstossen. Der andere Thread wurde dennoch beantwortet und ich bin dafuer sehr dankbar. Auch Dir Danke fuer den Kommentar, hierzu. Der Code sollte dennoch recht Compilerunabhaengig funktionieren, mein Compiler ist: gcc-4.3.2

    @LordJaxom
    Genau da gehen wohl meine Luecken in OOP los:
    - als Member(variable) verstehe ich eine Variable, Feld, etc, genauer die Instance von etwas in einer Klasse.
    - als Zeiger auf einen Member verstehe ich also einen Zeiger auf so eine Instance von irgendwas innerhalb einer Klasse

    Soweit so gut. Nur wenn es hier zu den Templates geht, wird es mir etwas unklar. Templates, zumindest in dieser Implementation - benoetigen einen Typ zum testen, keine Instanz!! Das kann also nicht die Loesung sein, oder?!

    Wenn ich zB

    &SomeClass::a
    

    hier einsetze, bekomme ich auch wie erwartet einen Compiler Error:

    SomeClass::a cannot appear in a constant-expression.
    

    Weil, 'a' ist eine Instanz und kein Typ. Nur was ist dann der "Typ eines Zeigers auf einen Member". Ich hatte da type-maessig eher noch an innere Klassen gedacht, aber dachte auch vllt haengt das eher damit zusammen, wo das typedef steht. Nur letzteres erschien mir eher esotherisch (abgesehn, dass es das nicht sein kann -> bekomme auch "false"). Irgendwie bin ich aber auch nicht in der Lage - das ist wohl mein Hauptproblem - den Code richtig zu lesen und zu interpretieren um zu verstehen was da eigentlich passiert. 😕



  • EDIT:
    Evtl. schaust Du Dir zuerst das Beispiel an, ich glaube die Erklärung ist ziemlich verworren geraten 😞

    Fabeltier schrieb:

    - als Member(variable) verstehe ich eine Variable, Feld, etc, genauer die Instance von etwas in einer Klasse.
    - als Zeiger auf einen Member verstehe ich also einen Zeiger auf so eine Instance von irgendwas innerhalb einer Klasse

    Ein Zeiger auf einen Member unterscheidet sich in keinster Weise von einem Zeiger auf eine freie Variable. Hier kann der Unterschied in der Begrifflichkeit also nicht liegen.

    "Pointer-to-member" ist weder ein Typ noch eine Variable. Vielmehr ist es ein Konstrukt, welches zusammen mit einem Objekt einen Member adressieren kann. Aus einem Pointer-to-member wird also, zusammen mit einem Objekt, ein Member.

    Eine Zeiger auf eine Instanz wäre z.B. &myobject.data . Das ist ein Zeiger, der auf den Member data des Objekts myobject zeigt, also auf eine konkrete Adresse. Ein Pointer-To-Member hingegen wäre z.B. &MyClass::data . Der zeigt aber nirgendwo hin, weil noch garnicht bekannt ist, wo das Objekt, dem der Member data gehört, überhaupt lebt.

    Aber Du hast Recht, der Zeiger-auf-X lässt sich hier im Beispiel natürlich nicht einsetzen, weil das eine konkrete Instanz eines Pointer-to-member ist (nicht zu verwechseln mit der konkreten Instanz eines Members eines beliebigen SomeClass). Der Typ, den Du übergeben müsstest, wäre (z.B.) int SomeClass::*

    Interessant wird das ganze aber wenn man den Compiler den Typ ermitteln lässt:

    template< typename T >
    struct is_member_pointer
    {
            static const bool value = false;
    };
    
    template< typename Result, typename Class >
    struct is_member_pointer< Result ( Class::* ) >
    {
            static const bool value = true;
    };
    
    struct SomeClass
    {
        int a;
    }
    
    template< typename MemberPointer >
    void foo( MemberPointer memptr )
    {
        cout << "is member pointer? " << is_member_pointer< MemberPointer >::value;
    }
    
    int main()
    {
        foo( &SomeClass::a );
        // entspricht dank Herleitung folgendem und ergibt "1"
        // foo< int SomeClass::* >( &SomeClass::a ); 
    
        foo( 12 );
        // entspricht dank Herleitung folgendem und ergibt "0"
        // foo< int >( 12 );
    }
    


  • Fabeltier schrieb:

    ...Der Code sollte dennoch recht Compilerunabhaengig funktionieren, mein Compiler ist: gcc-4.3.2...

    Leider ist das aber nicht so. 😉
    Aber soweit ich weiß, ist der gcc-4.3.2 ziemlich "template-fest". 😃

    Aber schön, dass der Lord Dir bereits geantwortet hat. 👍

    Gruß,

    Simon2.



  • ..und es funktioniert doch 😉 Es lag eben doch an mir und nicht am Compiler, jetzt laeuft es.

    Die Erklaerung trifft genau den Punkt den ich nicht verstanden hatte. Das Kernproblem war eben auch schon folgende Definition:

    template< class U, class V >
      struct Ptr2MemTraits< U V::* >
      {
    ...
    

    Oder genauer: U V::*

    Da wird also erstmal eine Partielle Template Spezialisierung gegeben (gut sowas hab ich auch erst gerade mehr oder weniger gelernt), ok, aber dann werden die Typen total vermixt und das Ergebnis sieht fast schon nach Perl aus, lol. Aber mit Deiner Erklaerung, mit Klammern U (V::*), wird das auf einmal klar, warum man das so schreibt.

    Nur eine Kleinigkeit, ich habe in meinem Beispiel ein enum verwendet, Du laesst das Ergebnis direkt ueber die structs laufen. Gut, aber ich sehe gar kein "struct"?! Irgendwie von C her kommend und gleich immer alle struct's geistig schon typedef-end wundert mich das ein bisschen. Muesste man nicht auch in C++ eigentlich schreiben

    cout << .... << struct is_member_pointer< MemberPointer >::value;
    

    Super Erklaerung! 😉



  • Fabeltier schrieb:

    Irgendwie von C her kommend und gleich immer alle struct's geistig schon typedef-end wundert mich das ein bisschen. Muesste man nicht auch in C++ eigentlich schreiben

    cout << .... << struct is_member_pointer< MemberPointer >::value;
    

    In C++ kann das struct -Keyword wegbleiben, der Compiler ist in den meisten Fällen selbst fähig zu erkennen, dass es sich bei dem namen um einen Typbezeichner handelt.


  • Administrator

    Vielleicht noch als Ergänzung zum Post von pumuckl:
    In C++ ist ein class und ein struct grundsätzlich das gleiche, nur hast du bei struct per default public und bei class per default private . Du kannst aber auch ein struct vererben oder erben lassen usw. Und daher musst du auch kein struct vorne dran schreiben, wie du auch kein class vor einem Klassenbezeichner schreiben musst. C++ ist halt nicht C! 😉

    Grüssli



  • Ergänzend sei noch gesagt, dass ich für Template-Metafunktionen immer struct nehme, weil man sich eben das public: sparen kann, und es ansonsten das gleiche ist wie class. Meist enthalten diese ja eh nur einen einzelnen Typ oder eine Konstante.

    Und enum-Konstanten hat man früher gerne genutzt, als Compiler integrale Konstanten ( static const bool/int/... in Klassen) noch nicht vernünftig beherrschten.



  • Danke, das mit dem "struct" in C++ hatte ich bisher nicht gewusst, hab mich nur gewundert, ob der Compiler da nicht meckern wuerde. Aber ich habe eben auch schon ein paar C++ Structs gesehn, die wie Klassen verwendet wurden, nur eben "by-default" public sind. Jetzt wird das etwas klarar fuer mich.

    Ich dachte das enum sei nur eine Moeglichkeit das fuer den Benutzer des Traits comfortabler zu gestalten, danke auch fuer die Ausfuehrung!

    👍 👍 👍



  • Fabeltier schrieb:

    Aber ich habe eben auch schon ein paar C++ Structs gesehn, die wie Klassen verwendet wurden, nur eben "by-default" public sind.

    Genau das ist AFAIK der einzige wirkliche Unterschied zwischen struct und class in C++, nämlich dass bei der Definition die default-Zugriffsrechte public bzw. private sind. Alles weitere ist in Bezug auf Klassendeklarationen und -definitionen identisch, man kann sogar einen einmal als struct deklarierten Typ als class definieren und umgekehrt:

    class C; //forward-declaration
    struct S;
    
    struct C   //implementation des als class deklarierten typs
    {
      /* per default alles public, da die Definition als struct erfolgt */
      /* dass die Deklaration vorher class hieß ist wurscht */
    };
    
    class S
    {
      /* ... */
    };
    

    Der einzige weitere Unterschied der mir einfällt ist, dass man bei er Deklaration von template-Parametern an Stelle des typename zwar ein class benutzen kann, aber kein struct:

    template <typename T> void f(); //deklaration
    template <class T> void f(); //nochmal die selbe deklaration
    template <struct T> void f(); //ERROR! struct nicht erlaubt...
    

Log in to reply