The problem is that here:
friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
You declare a function without a template (which will be different for each Edge instance) as a friend , and not create a template.
The most common solution I saw was to simply embed the operator<< built-in string in the class definition template. In addition, you can provide a public member function that outputs and is called from the operator<< function template. Or you can write:
friend ostream& operator<< <Vertex>(ostream&, Edge<Vertex> const& );
to tell the compiler that operator<< , which is a friend, is creating a template. IIUC, however, this only works if there is an declaration of the objective function operator<< At this point, the template is displayed, which means that you need to forward declare it (and in order to direct it, forward declare the class template).
My usual solution for this kind of task is to provide a normal print member function, and then infer from:
template <typename DerivedType> class IOStreamOperators { public: friend std::ostream&operator<<( std::ostream& dest, DerivedType const& source ) { source.print( dest ) ; return dest ; } friend std::istream&operator>>( std::istream& source, DerivedType& dest ) { dest.scan( source ) ; return source ; } protected: ~IOStreamOperators() {} };
eg:
template <class Vertex> class Edge : public IOStreamOperators<Edge<Vertex> > { // ... void print( std::ostream& dest ) { // ... } };
I found that this makes the code simpler and easier to follow at the end.