klassentemplate in klassentemplate



  • hallo

    ich versuche gerade einen code im stil von dem hier zum laufen zu bekommen (minimalbeispiel):

    template<class T>
    struct foo
    {
    	template<class U>
    	struct bar
    	{
    		int val;
    		bar(int n) : val(n) {}
    	};
    };
    template<class T, class U>
    foo<T>::bar<U> operator+ (int lhs, foo<T>::bar<U> rhs)
    {
    	rhs.val += lhs;
    	return rhs;
    }
    #include <iostream>
    int main()
    {
    	foo<char>::bar<int> tester(5);
    	std::cout << (2 + tester).val;
    }
    

    ausgabe:

    Compilation error	 time: 0 memory: 3428 signal:0
    prog.cpp:12:44: error: ‘foo<T>::bar’ is not a type
     foo<T>::bar<U> operator+ (int lhs, foo<T>::bar<U> rhs)
                                                ^
    prog.cpp:12:47: error: expected ‘,’ or ‘...’ before ‘<’ token
     foo<T>::bar<U> operator+ (int lhs, foo<T>::bar<U> rhs)
                                                   ^
    prog.cpp:12:54: error: ‘foo<T>::bar<U> operator+(int, int)’ must have an argument of class or enumerated type
     foo<T>::bar<U> operator+ (int lhs, foo<T>::bar<U> rhs)
                                                          ^
    prog.cpp: In function ‘int main()’:
    prog.cpp:21:18: error: no match for ‘operator+’ (operand types are ‘int’ and ‘foo<char>::bar<int>’)
      std::cout << (2 + tester).val;
                      ^
    

    dass bar kein typ sondern ein template ist ist mir auch bewusst, soll der compiler halt bis zur template-argumentenliste weiterlesen...

    was kann ich da machen damit das funktioniert?


  • Mod

    template<class T>
    template<class U>
    typename foo<T>::template bar<U> operator+ (int lhs, typename foo<T>::template bar<U> rhs)
    {
    ...
    


  • sowas ähnliches hab ich auch schon versucht, jedoch kommt vom compiler dann immer

    prog.cpp:13:34: error: too many template-parameter-lists
     typename foo<T>::template bar<U> operator+ (int lhs, typename foo<T>::template bar<U> rhs)
                                      ^
    

  • Mod

    ah sorry,

    template<class T, class U>
    typename foo<T>::template bar<U> operator+ (int lhs, typename foo<T>::template bar<U> rhs)
    

    nat. nur eine Templateparameterliste. Wir wollen ja kein Membertemplate definieren. Funktionieren wird das trotzdem nicht, weil T so nicht deduziert werden kann.



  • Wenn du eine Template-Klasse hast, Operatoren immer als friend deklarieren, nie als Template

    template<class T>
    struct foo
    {
      template<class U>
      struct bar
      {
        int val;
        bar(int n) : val(n) {}
    
        friend bar operator+ (int lhs, bar rhs)
        {
          rhs.val += lhs;
          return rhs;
        }
      };
    };
    

    Grund dazu ist, dass nur so implizite Konvertierungen greifen (weil die friend-Funktion bei der Instanzierung der Klasse generiert wird). Du hättest auch schreiben können

    friend bar operator+ (bar lhs, bar rhs)
        {
          rhs.val += lhs.val;
          return rhs;
        }
    

    und du könntest trotzdem "5 + tester" berechnen, weil 5 in bar konvertiert werden kann.


  • Mod

    Wenn du eine Template-Klasse hast, Operatoren immer als friend deklarieren, nie als Template

    Das ist Unsinn. Die Regel muss ganz anders formuliert werden:
    Überlädt man einen binären Operator für eine Klasse, so bietet es sich manchmal an, diesen als non-member zu überladen, damit implizite Konvertierungen greifen.



  • Arcoth schrieb:

    Wenn du eine Template-Klasse hast, Operatoren immer als friend deklarieren, nie als Template

    Das ist Unsinn. Die Regel muss ganz anders formuliert werden:
    Überlädt man einen binären Operator für eine Klasse, so bietet es sich manchmal an, diesen als non-member zu überladen, damit implizite Konvertierungen greifen.

    Bring mal ein Beispiel, wann das gewünscht ist und es einen impliziten Konstruktor braucht.


  • Mod

    Moment, hab' ich deinen Tipp falsch interpretiert?
    Ich dachte, du beziehst dich auf das Member-Sein, nicht auf das Deklarieren außerhalb, denn direkt danach hast du ja genau den Grund genannt 😕

    Allerdings ist mein ursprünglicher Einwand weiterhin richtig; es sei Unsinn, u.a. da man nie alles standardmäßig als friend deklariert, auch wenn man dann ein wenig Tipparbeit spart. Das ist eine Sünde. Man darf nicht einfach um Tipparbeit zu sparen die Kapselung lockern.


  • Mod

    camper schrieb:

    Funktionieren wird das trotzdem nicht, weil T so nicht deduziert werden kann.

    Ja, ich verstehe im Übrigen nicht, warum das ein non-deduced context ist:

    §14.8.2.5/5 schrieb:

    — The nested-name-specifier of a type that was specified using a qualified-id.

    Könntest du mir das erklären?



  • [in Klassentemplates:] Operatoren immer als friend deklarieren

    sollte ziemlich klar sein, ich kann mir nicht vorstellen, wie man da etwas falsch interpretieren könnte.

    Arcoth schrieb:

    Allerdings ist mein ursprünglicher Einwand weiterhin richtig; es sei Unsinn, u.a. da man nie alles standardmäßig als friend deklariert, auch wenn man dann ein wenig Tipparbeit spart. Das ist eine Sünde. Man darf nicht einfach um Tipparbeit zu sparen die Kapselung lockern.

    Es wird nirgendwo Kapselung gelockert. Vielleicht formal gesehen lokal in den 2 Zeilen des Operators, aber das ist nicht schlimm. Das hat auch nichts mit Kapselung zu tun.

    Und nebenbei bemerkt gibt es (normalerweise, auch hier) keine Alternative. Das hat nichts mit Tipparbeit zu tun.


  • Mod

    Arcoth schrieb:

    camper schrieb:

    Funktionieren wird das trotzdem nicht, weil T so nicht deduziert werden kann.

    Ja, ich verstehe im Übrigen nicht, warum das ein non-deduced context ist:

    §14.8.2.5/5 schrieb:

    — The nested-name-specifier of a type that was specified using a qualified-id.

    Könntest du mir das erklären?

    weil es im Allgemeinen nicht möglich ist

    template <typename T>
    struct strange
    {
        using type = T*;
    };
    template <typename T>
    struct strange<T*>
    {
        using type = T;
    };
    
    template <typename T>
    void foo(typename strange<T>::type);
    
    ...
    int* x;
    foo(x); // T=int oder T = int** ?
    

  • Mod

    Vielleicht formal gesehen lokal in den 2 Zeilen des Operators, aber das ist nicht schlimm.

    Doch.

    Und nebenbei bemerkt gibt es (normalerweise, auch hier) keine Alternative.

    Leider sind injected-class-names nicht in inneren Klassen verfügbar... 😡

    sollte ziemlich klar sein, ich kann mir nicht vorstellen, wie man da etwas falsch interpretieren könnte.

    Folglich ist deine Begründung dann irgendwie unpassend:

    Wenn du eine Template-Klasse hast, Operatoren immer als friend deklarieren, nie als Template [...]
    Grund dazu ist, dass nur so implizite Konvertierungen greifen

    weil es im Allgemeinen nicht möglich ist

    Das ist mir auch klar. Aber da, wo es nicht möglich ist, könnte man es ill-formed machen, und wo nicht, da nicht. Ist die Entität, auf die die qualified-id sich bezieht, ein UDT innerhalb jener Klasse, ist es eindeutig.


Log in to reply