Reputation: 39871
Why I cannot use the same template parameter for a friend function that takes a template argument? I mean the code below is OK!
template <class Vertex>
class Edge
{
template <class T>
friend ostream& operator<<(ostream& os, const Edge<T>& e);
/// ...
};
template <class T>
ostream& operator<<(ostream& os, const Edge<T>& e)
{
return os << e.getVertex1() << " -> " << e.getVertex2();
}
But this one is NOT ok. Why? What is the problem? (I get linker error.)
template <class Vertex>
class Edge
{
friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
/// ...
};
template <class T>
ostream& operator<<(ostream& os, const Edge<T>& e)
{
return os << e.getVertex1() << " -> " << e.getVertex2();
}
Upvotes: 6
Views: 322
Reputation: 106096
I think it's easiest to understand if we remove the extraneous noise and consider:
template <typename T>
struct X
{
friend void f(X<T>& x) { }
};
template <typename T>
void f(const X<T>& x) { }
f
inside X
is: void f(X<T>& x)
f
outside X
is: void f<T>(X<T>& x)
You can get a hint of this by compiling and looking at the symbols generated:
00410aa8 t .text$_Z1fR1XIdE
00410ad4 t .text$_Z1fIdEvRK1XIT_E
Calling GCC's __PRETTY_FUNCTION__ from each yields:
void f(X<double>&)
void f(const X<T>&) [with T = double]
Not particularly clear, but GCC's way of saying the latter's void f<double>(...)
.
Personally, for templates I tend to define the function in the class... you don't need to mention the template aspect at all, just:
friend ostream& operator<<(ostream& os, const Edge& e)
{
// use e.whatever...
}
Upvotes: 1
Reputation: 153919
The problem is that here:
friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
You are declaring a non-template function (which will be
different for each instantiation of Edge
) to be a friend
,
and not the instantiation of the template.
The most common solution here that I've seen has been to simply
implement the operator<<
inline, in the class template
definition. Alternatively, you can provide a public member
function which does the output, and invoke it from the
operator<<
function template. Or you can write:
friend ostream& operator<< <Vertex>(ostream&, Edge<Vertex> const& );
to tell the compiler that the operator<<
which is a friend is
an instantiation of a template. IIUC, however, this only works
if there is a declaration of the targetted operator<<
function
template visible at this point, which means that you need to
forward declare it (and in order to forward declare it, to
forward declare the class template).
My usual solution for this sort of problem is to provide
a normal member function print
, and then derive 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() {}
};
, e.g.:
template <class Vertex>
class Edge : public IOStreamOperators<Edge<Vertex> >
{
// ...
void print( std::ostream& dest )
{
// ...
}
};
I've found that this generally makes the code simpler and easier to follow in the end.
Upvotes: 2
Reputation: 55887
You can use following
template <class Vertex>
class Edge
{
friend ostream& operator<< <>(ostream& os, const Edge<Vertex>& e);
/// ...
};
that makes operator << <Vertex>
friend to Edge
.
In your second case - you make friend non-template operator, but definition of this operator is template, so you have undefined reference, but this case can be used if you want your operator <<
for concrete Edge
(Edge<int>
for example).
Upvotes: 3
Reputation: 56479
template <class T>
friend ostream& operator<<(ostream& os, const Edge<T>& e);
Says, there is templated operator <<
outside, be friend with it, and everything is OK.
friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
Says, there is a operator <<
outside, be friend with it... and compiler can not find such a thing.
To tell compiler that the operator is templated, help him by a <>
as ForEveR mentioned (He beat me rapidly :-D ).
Upvotes: 2