template-friends: Bug in compiler



  • Hallo

    VC8 akzeptiert folgenden Code, gcc 4.3 nicht. Wieso?

    template<class A> void foo2(A)
    {
    }
    template<class A,template<class>class B> void foo(B<A>)
    {
    }
    template<class> class bar
    {
    	template<class A,template<class>class B> friend void foo(B<A>);
    	template<class A> friend void foo2(A);
    };
    
    int main(int num_arguments, char* arguments[])
    {
    	foo2(bar<int>());//OK
    	foo(bar<int>());/*main.cpp:308: error: call of overloaded 'foo(bar<int>)' is ambiguous
    main.cpp:298: note: candidates are: void foo(B<A>) [with A = int, B = bar]
    main.cpp:303: note:                 void foo(B<A>) [with A = int, B = bar, <template-parameter-1-1> = int]*/
    }
    

    ::oo(bar<int>()) funktioniert zwar, aber diese Funktion ist kein friend von der bar-Klasse. Anscheinend gibt es also zwei verschiedene foo-Deklarationen.

    Danke!



  • Ich pushe mal. Falls bis heute abend keiner eine Idee hat, werde ich mal ein testcase vorbereiten und an den gcc bugtracker schicken.


  • Administrator

    Das ist meiner Meinung nach kein Fehler, sondern ist Teil des sehr komplizierten Standards für Friend-Function-Templates. In deiner Klasse bar wird tatsächlich eine neue Funktion foo erzeugt, allerdings nur eine neue Deklaration ohne Definition. Um dies zu verhindern, muss du die Funktion explizit angeben:

    template<typename T1, template<class> class T2>
    void foo(T2<T1>)
    {
    }
    
    template<typename>
    class Bar
    {
      template<typename T1, template<class> class T2>
      friend void ::foo(T2<T1>);
    
      //          ^^
      //          ||
      // Dieser Scope-Operator ist entscheidend.
    }
    

    Wieso es bei deinem foo2 funktioniert, bin ich mir nicht ganz sicher. Aber es könnte womöglich an ADL (Argument Dependent Lookup) liegen, dass es nämlich die zweite Deklaration von foo2 in bar bevorzugt. Würde aber bedeuten, dass es beim Linken zu einem Fehler geführt hätte, da es keine Definition gibt. Allerdings rate ich hier jetzt eher, als das ich es mit Sicherheit weiss. Vielleicht kann noch jemand anderes etwas dazu beisteuern.

    Edit: Fehlerkorrektur und noch etwas zusätzliches
    Buchempfehlung:
    C++ Templates - The Complete Guide
    von
    David Vandevoorde & Nicolai M. Josuttis

    Grüssli



  • zwei vorgestellte Doppelpunkte in der Friend-Deklaration habe ich schon probiert - das hat nichts gebracht.

    [quote=Dravere ]In deiner Klasse bar wird tatsächlich eine neue Funktion foo erzeugt, allerdings nur eine neue Deklaration ohne Definition. Um dies zu verhindern, muss du die Funktion explizit angeben: [/quote]
    Es ist so oder so eine Deklaration - dadurch dass ich die Signatur mit einem ; endenen lasse statt mit einer Definition (z.B. durch { und } ). Das :: ist dann nur ein expliziter Scope-bezeichner, wobei der überflüssig ist, da ja implizit der aktuelle scope (in meinem Fall global namespace) genutzt wird.

    Das Problem ist in meinen Augen, dass das Frontend eine Abhängigkeit zu dem Klassen-Templateparameter einfügt (wieso auch immer?), und demnach gibt es zwei Deklarationen (zeigt ja auch die Fehlermeldung). Afaik ist dies falsch?!



  • BlubDerBlubber schrieb:

    Dravere schrieb:

    In deiner Klasse bar wird tatsächlich eine neue Funktion foo erzeugt, allerdings nur eine neue Deklaration ohne Definition. Um dies zu verhindern, muss du die Funktion explizit angeben:

    Es ist so oder so eine Deklaration - dadurch dass ich die Signatur mit einem ; endenen lasse statt mit einer Definition (z.B. durch { und } ). Das :: ist dann nur ein expliziter Scope-bezeichner, wobei der überflüssig ist, da ja implizit der aktuelle scope (in meinem Fall global namespace) genutzt wird.

    Ähh, sorry, habe dich falsch verstanden. Ignorier den ersten Satz 😉


  • Administrator

    Ich habe leider keinen g++ hier und der MSVC ist viel zu nett mit dem Programmierer und vereinfacht diese verteufelte friend Deklaration, daher kann ich grad schlecht testen. Aber ich bin nochmal über das Buch gegangen. Eine Möglichkeit wäre noch das Folgende:

    template<typename T1, template<class> class T2>
    void foo(T2<T1>)
    { 
    }
    
    template<typename>
    class Bar
    {
      template<typename T1, template<class> class T2>
      friend void foo<T1, T2>(T2<T1>);
    }
    

    Oder dies:

    template<typename T1, template<class> class T2>
    friend void foo<>(T2<T1>);
    

    Oder eine der beiden mit entsprechendem Scope-Operator davor 🙂
    Einfach mal ausprobieren 😉

    Grüssli



  • hehe - alles schon ausprobiert 😉

    template<class A,template<class>class B> void foo(B<A>)
    {
    }
    template<class> class bar
    {
    	template<class A,template<class>class B> friend void foo<>(B<A>);//error: invalid use of template-id 'foo<>' in declaration of primary template
    	template<class A,template<class>class B> friend void ::foo<>(B<A>);//error: invalid use of template-id 'foo<>' in declaration of primary template
    	template<class A,template<class>class B> friend void foo<A,B>(B<A>);//error: invalid use of template-id 'foo<A, template<class> template<class> class B>' in declaration of primary template
    	template<class A,template<class>class B> friend void ::foo<A,B>(B<A>);//error: invalid use of template-id 'foo<A, template<class> template<class> class B>' in declaration of primary template
    };
    
    int main(int num_arguments, char* arguments[])
    {
        foo(bar<int>());
    }
    

    btw.: gcc 4.3.3 für Windows:

    http://www.tdragon.net/recentgcc
    

    Läuft bei mir seit monaten tadellos, selbst in produktiven Umgebungen!




Anmelden zum Antworten