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.
-
Das ist meiner Meinung nach kein Fehler, sondern ist Teil des sehr komplizierten Standards für Friend-Function-Templates. In deiner Klasse
barwird tatsächlich eine neue Funktionfooerzeugt, 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
foo2funktioniert, 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 vonfoo2inbarbevorzugt. 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. JosuttisGrü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

-
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/recentgccLäuft bei mir seit monaten tadellos, selbst in produktiven Umgebungen!
-
Mal schaun, was draus wird.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39328