export + operator<<
-
Kann mir jemand sagen, was an folgendem Code falsch ist:
class.H
#include <iostream> export template<typename T> class foo { public: explicit foo(const T& my_t); void print(); private: const T& t_; friend std::basic_ostream<char>& operator << (std::basic_ostream<char>&, const foo<T>&); }; export template <typename T> std::basic_ostream<char>& operator << (std::basic_ostream<char>&, const foo<T>&);
class.C
#include "class.H" #include <typeinfo> template <typename T> foo<T>::foo(const T& my_t) : t_(my_t) {} template <typename T> void foo<T>::print() { std::cout << "foo<" << typeid(t_).name() << "> " << t_ << '\n'; } template <typename T> std::basic_ostream<char>& operator << (std::basic_ostream<char>& str, const foo<T>& foo_) { return (str << typeid(foo_.t_).name() << ':' << foo_.t_); }
main.C
//#include "fun.hxx" #include "class.H" #include <iostream> int main() { foo<int> myfoo(12); myfoo.print(); std::cout << myfoo << '\n'; std::cout.flush(); }
foo<T>::print() funktioniert wunderbar, aber für den Operator bekomme ich
main.o: In function `main': main.3087.int.c:(.text+0x39): undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, foo<int> const&)' collect2: ld returned 1 exit status
(Compiler ist Comeau C/C++ 4.3.3 (Oct 24 2003 16:00:23) for RedHat_LINUX_INTEL_ELF)
-
wenn ich class.C auf
#include <iostream> export template <typename T> class foo; export template <typename T> std::basic_ostream<char>& operator<< (std::basic_ostream<char>&, const foo<T>&); export template<typename T> class foo { public: explicit foo(const T& my_t); void print(); private: const T& t_; friend std::basic_ostream<char>& operator<< <T>(std::basic_ostream<char>&, const foo<T>&); };
ändere geht's
-
Hmm. Benutzt du Comeau?
Ansonsten wäre der "Fehler" wahrscheinlich bei export.btw (oder eher der Hauptgrund, warum isch schreibe):
Du hast eine coole Anzahl Beiträge. ;)!!!111
-
Durch die korrigierte Version ist wahrscheinllich schon klar geworden, wo das Problem lag. Die ursprüngliche friend Deklaration deklariert eine Funktion, die Definition, die auf die Klassentemplatedefinition folgt, definiert aber ein Funktionstemplate. Diese haben also nichts miteinander zu tun und führen nur zu Überladung. Da andererseits zu jeder Spezialisierung eine signaturgleiche normale Funktion existiert, wird keine Spezialisierung des Templates jemals bei normalen Funktionsaufruf (ohne explizite Angabe eines Templatesparamters) ausgewählt werden. Die Definition der friend-Funktionen außerhalb der Definition des Klassentemplates kann aber nur für jede mögliche Signatur einzeln erfolgen, deshalb wird man diese Form nur benutzen, wenn die Funktion unmittelbar in der Klassentemplatedefinition inline definiert werden kann.
Eine mögliche andere Variante wurde gezeigt - hier ist der friend eine explite Spezialisierung eines Funktionstemplates. Spezialisierungen können nur deklariert werden, nachdem das primäre Template deklariert wurde. Deshalb die zusätzlich nötigen separaten Deklarationen. Alternativ könnte man das gesamte Funktionstemplate als friend deklarieren (und sinnvollerweise auf die friend-Deklaration der Spezialisierung verzichten, da sie dann überflüssig ist).template<typename T> class foo { ... template<typename U> friend std::basic_ostream<char>& operator<<(std::basic_ostream<char>&, const foo<U>&);
Auf einen anderen Fehler möchte ich noch hinweisen: obiges Programm hat undefiniertes Verhalten, da das temporäre Objekt, das erzeugt wird, um beim Aufruf des foo<int>-Konstruktors an den Funktionsparameter gebunden zu werden, nach Beendigung der Konstruktion von myfoo zerstört wird. Die folgende Verwendung des Wertes des Members t_ ist folglich undefiniert, da diese Referenz nicht mehr auf reservierten Speicher zeigt (der typeid-Ausdruck ist davon nicht betroffen).