简体   繁体   中英

operator<<(ostream& os, …) for template class

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();
}

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).

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 ).

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() {}
};

, eg:

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.

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) { }
  • The f inside X is: void f(X<T>& x)
  • The 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...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM