The problem is that you have two different operator<<. One inside the class (just a prototype) and one outside the class. For the compiler they are not related.
template < typename T > struct pt1
{
T x {} ;
T y {} ;
friend std::ostream& operator<< ( std::ostream& stm, const pt1& p )
{ return stm << '(' << p.x << ',' << p.y << ')' ; }
};
Or clarify that the friend function is a template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template < typename T > struct pt2 ; // declare the class template
template < typename T > std::ostream& operator<< ( std::ostream& stm, const pt2<T>& p ) ; // declare the function template
template < typename T > struct pt2
{
T x {} ;
T y {} ;
// add <> after the name of the function to inform the compiler that the friend
// declaration declares a template (without <>, it declares a non-template)
friend std::ostream& operator<< <> ( std::ostream& stm, const pt2<T>& p ) ;
};
template < typename T > std::ostream& operator<< ( std::ostream& stm, const pt2<T>& p )
{ return stm << '(' << p.x << ',' << p.y << ')' ; }
Yet another possibility is the unbound friend function template to the class template – by declaring a function template inside the class template you can create unbound friend functions for which every function specialization is a friend to every class specialization. For unbound friends, the friend template type parameters are different from the class template type parameters:
However there is one downside to this approach – any particular instantiation of pt2<T> provides access to all instantiations of the operator << overloads i.e. std::ostream& operator << (std::ostream& , const pt2<std::string>&) has access to all internals of pt2<int> and so on … this is precluded by the second approach in the previous post – a single instantiation of the function template is now declared to be a friend of the class template without exposing the other instantiations