Yunfei  Duan
Yunfei Duan

Reputation: 43

why does g++ need both definitions of template class and its friend function?

I want to write a friend function for a template class. In visual studio, I can ignore the pre-definition both. But in g++, it is mandatory. Why?

#include <iostream>
using namespace std;
// g++ needs, vs do not needs
template <class T>
class A;

template <class T>
ostream & operator<<(ostream & c, const A<T> & v);
//- end of g++ needs

template <class T>
class A {
    T _v;
public:
    A() {}
    A(T v) : _v(v) {}
    friend ostream & operator<<<T>(ostream & c, const A<T> & v);
};
template <class T>
ostream & operator<<(ostream & c, const A<T> & v) {
    c << v._v; return c;
}

Upvotes: 2

Views: 91

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275760

MSVC template code is fundamantally broken. They have been rebuilding it over the last few years, but it still contains quirks like what you are observing. This is because it was built to be almost macro like, instead of being what the C++ standard required.

In gcc you can do away with the forward declarations by defining the << operator inline in the body of the class:

friend std::ostream& operator<<(std::ostream& c, const A& v){
  c << v._v;
  return c;
}

this has the advantage that << becomes no longer a template, but rather a distinct non-template created for each instance of the template A. In some scenarios this tends to work better.

Upvotes: 0

Surt
Surt

Reputation: 16109

Because

friend ostream & operator<<<T>(ostream & c, const A<T> & v);

is a specialization of

template <class T>
ostream & operator<<(ostream & c, const A<T> & v);

you need to declare that first and the

A<T>

part means you have to declare that too before the operator declaration

template <class T>
class A;

So VS is probably wrong as C++14

14.5.4 Friends [temp.friend] 

gives the example

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);

template<class T> class task {
  friend void next_time();
  friend void process(task<T>*);
  friend task<T>* preempt<T>(task<T>*);
  template<class C> friend int func(C);

  friend class task<int>;
  template<class P> friend class frd;
};

Where your example fits the 3rd friend declaration.

Upvotes: 2

Related Questions