Wie trennt man Deklaration und Definition von friend-member-template-Funktionen?
-
Ulrich_B schrieb:
brotbernd schrieb:
template <typename STREAM> friend STREAM& operator<<(STREAM& os, const Test<T>& rhs);Du deklarierst hier eine Familie von << operatoren als
friendder Klasse Test<T>. Diese sind alle freie Funktionen.
Zu jeder Test<T> gibt es also eine Reihe von operatoren für verschiedene STREAM Typen.template<typename T, typename STREAM> STREAM& operator<<(STREAM& os, const Test<T>& rhs) { os << rhs.value_; return os; }Definiert für verschiedene Typen T operatoren für verschiedene Typen STREAM.
Was meckert der Compiler denn? Ich hab hier grad nur den msvc9, der schluckt das.
Der GCC Compiler selbst findet das in Ordnung. Anscheinend nutzt er jedoch andere operator<<-Versionen als die Implementierung im Beispiel zur Verfügung stellen könnte so dass der Linker Fehler meldet:
`
test.o: test.cpp:(.text+0xb4): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& operator<< <std::basic_ostream<char, std::char_traits<char> > >(std::basic_ostream<char, std::char_traits<char> >&, Test<int> const&)'
`
Was ist daran verwunderlich? Natürlich kann nicht gelinkt werden, es sind ja zwei unterschiedliche Funktionen. Und für eine gibt es keine Definition, welche aber gebraucht wird. Die andere Funktion ist überflüssig.
Das ist quasi das gleiche wie hier:
void print(int); void print(char**) { /* print char** */ } main() { print(3); }
-
Was mich wundert ist dass der MSVC das angeblich schluckt.
-
sorry leute.. mein Fehler.
Ich hatte (wie man in meinem 1.post sieht)value_nicht privat. Kein wunder, dass das funktioniert.
Naja ich war aufm Sprung in die Mittagspause...poste nicht mit leerem Magen ... wie peinlich
Ich mach es mit stream Operatoren eh lieber so
template <typename T> class Test { public: Test(const T&); template<typename STREAM> STREAM& print(STREAM& os) const { os << value_; return os; } private: T value_; }; template <typename T> Test<T>::Test(const T& value) : value_(value) { } template<typename STREAM, typename T> STREAM& operator<<(STREAM& os, const Test<T>& rhs) { return rhs.print(os); }Aber das war ja nicht die Frage...
-
Yeequ schrieb:
Was ist daran verwunderlich? Natürlich kann nicht gelinkt werden, es sind ja zwei unterschiedliche Funktionen. Und für eine gibt es keine Definition, welche aber gebraucht wird. Die andere Funktion ist überflüssig.
Das ist quasi das gleiche wie hier:
void print(int); void print(char**) { /* print char** */ } main() { print(3); }Das wäre noch treffender, wenn du print(0) geschrieben hättest.
Die zusätzlich deklarierte (und zudem definierte) Operatorüberladung ist ja durchaus ein Kandidat. Nur ist eintemplate <typename STREAM> STREAM& operator<<(STREAM& os, const Test<T>& rhs);für beliebige T eben spezieller als
template<typename STREAM, typename T> STREAM& operator<<(STREAM& os, const Test<T>& rhs)
-
camper schrieb:
Yeequ schrieb:
Was ist daran verwunderlich? Natürlich kann nicht gelinkt werden, es sind ja zwei unterschiedliche Funktionen. Und für eine gibt es keine Definition, welche aber gebraucht wird. Die andere Funktion ist überflüssig.
Das ist quasi das gleiche wie hier:
void print(int); void print(char**) { /* print char** */ } main() { print(3); }Das wäre noch treffender, wenn du print(0) geschrieben hättest.
Die zusätzlich deklarierte (und zudem definierte) Operatorüberladung ist ja durchaus ein Kandidat. Nur ist eintemplate <typename STREAM> STREAM& operator<<(STREAM& os, const Test<T>& rhs);für beliebige T eben spezieller als
template<typename STREAM, typename T> STREAM& operator<<(STREAM& os, const Test<T>& rhs)Ich bin vor 15 Minuten auch darauf gekommen, daß die Priorisierung der stärker spezialisierten Form innerhalb der Template-Klasse der Grund ist, warum man nur noch alle Spezialisierungen einzeln von Hand angeben kann und das ganze nicht mehr generisch machen kann. An die Priorisierung hatte ich in diesem Fall nicht gedacht. Steht aber genau so unter ISO 14882:2003 Abschnitt 14.5.3.1 beschrieben.
-
Yeequ schrieb:
Ich bin vor 15 Minuten auch darauf gekommen, daß die Priorisierung der stärker spezialisierten Form innerhalb der Template-Klasse der Grund ist, warum man nur noch alle Spezialisierungen einzeln von Hand angeben kann und das ganze nicht mehr generisch machen kann. An die Priorisierung hatte ich in diesem Fall nicht gedacht. Steht aber genau so unter ISO 14882:2003 Abschnitt 14.5.3.1 beschrieben.
Okay., es kann an dieser Stelle überhaupt nichts mit der Priorisierung von Templatespezialisierungen zu tun haben. Eine normale Friendfunktion ist an dieser Stelle ja auch kein Template.
-
Yeequ schrieb:
Priorisierung von Templatespezialisierungen
So etwas gibt es auch nicht, bzw. der Begriff ist so unscharf, dass ich nichts damit anfangen kann.
Yeequ schrieb:
Eine normale Friendfunktion ist an dieser Stelle ja auch kein Template.
Ich kann nicht folgen.
-
camper schrieb:
Yeequ schrieb:
Priorisierung von Templatespezialisierungen
So etwas gibt es auch nicht, bzw. der Begriff ist so unscharf, dass ich nichts damit anfangen kann.
Yeequ schrieb:
Eine normale Friendfunktion ist an dieser Stelle ja auch kein Template.
Ich kann nicht folgen.
Sorry. Ich habe gerade nicht genau nachgedacht als mir das eingefallen ist. Ich war gerade nebenbei mit etwas anderem beschäftigt. Das tut mir Leid.
Also es gibt schon eine Priorisierung bei der Templatespezisierung allerdings gibt es Spezialisierung nur für Template-Klassen, nicht für Funktionen:template <typename T> class iterator {}; template <typename T*> class iterator {};Ich weiß nur halt gerade immer noch nicht genau, warum man zwar die Varianten einzeln definieren kann
template <typename T> struct Foo { template<typename S> friend S& operator<<(S& os, const Foo& rhs); }; template<typename S> S& operator<<(S& os, const Foo<int>& rhs) { /* ... */ } template<typename S> S& operator<<(S& os, const Foo<float>& rhs) { /* ... */ }Aber eben nicht nochmal irgendwie als Template bzw. warum genau dann stets der Linkage Fehler kommt.
Mir war nur gerade wieder folgendes in den Sinn gekommen, wie man Template-Funktionen befreundet, die außerhalb der Klasse definiert sind, worauf mir der Gedanke kam, irgendwie könnte das ja vielleicht auch in dem ursprünglichen Fall so ähnlich gehen.
template <typename T> void func0(Test<T> x) { std::cout << "func0: " << x.value_ << std::endl; } template <typename T> class Test { public: Test(const T& value) : value_(value) {} friend void func0<>(Test); private: T value_; };
-
Yeequ schrieb:
Ich weiß nur halt gerade immer noch nicht genau, warum man zwar die Varianten einzeln definieren kann
template <typename T> struct Foo { template<typename S> friend S& operator<<(S& os, const Foo& rhs); }; template<typename S> S& operator<<(S& os, const Foo<int>& rhs) { /* ... */ } template<typename S> S& operator<<(S& os, const Foo<float>& rhs) { /* ... */ }Aber eben nicht nochmal irgendwie als Template bzw. warum genau dann stets der Linkage Fehler kommt.
Überleg einfach mal, welches Verhältnis zwischen
template<typename T> void foo(T, int);und
template<typename T, typename U> void foo(T, U);besteht? Spezialisierung? Überladung? Etwas anderes?
-
camper schrieb:
Yeequ schrieb:
Ich weiß nur halt gerade immer noch nicht genau, warum man zwar die Varianten einzeln definieren kann
template <typename T> struct Foo { template<typename S> friend S& operator<<(S& os, const Foo& rhs); }; template<typename S> S& operator<<(S& os, const Foo<int>& rhs) { /* ... */ } template<typename S> S& operator<<(S& os, const Foo<float>& rhs) { /* ... */ }Aber eben nicht nochmal irgendwie als Template bzw. warum genau dann stets der Linkage Fehler kommt.
Überleg einfach mal, welches Verhältnis zwischen
template<typename T> void foo(T, int);und
template<typename T, typename U> void foo(T, U);besteht? Spezialisierung? Überladung? Etwas anderes?
Also wenn ich diese letzten zwei Funktionen untereinander sehe, dann wird mir auch sofort bewußt, daß die friend-temlate-Deklaration eine Funktion von erstem Muster in die Funktionstabelle einträgt, wohingegen die zweite Funktion zwei Template-Argumente hat. Die Overload Resolution wählt dann natürlich die erste Funktion aus, die aber nicht definiert ist.
Eigentlich ist es sehr einfach, wenn man seine Gedanken richtig ordnet.