61

I have a base Class akin to the code below. I'm attempting to overload << to use with cout. However, g++ is saying:

base.h:24: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, Base<T>*)’ declares a non-template function
base.h:24: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

I've tried adding <> after << in the class declaration / prototype. However, then I get it does not match any template declaration. I've been attempting to have the operator definition fully templated (which I want), but I've only been able to get it to work with the following code, with the operator manually instantiated.

base.h

template <typename T>
class Base {
  public:
    friend ostream& operator << (ostream &out, Base<T> *e);
};

base.cpp

ostream& operator<< (ostream &out, Base<int> *e) {
    out << e->data;
return out;
}

I want to just have this or similar in the header, base.h:

template <typename T>
class Base {
  public:
    friend ostream& operator << (ostream &out, Base<T> *e);
};

template <typename T>
ostream& operator<< (ostream &out, Base<T> *e) {
    out << e->data;
return out;
}

I've read elsewhere online that putting <> between << and () in the prototype should fix this, but it doesn't. Can I get this into a single function template?

Chubsdad
  • 24,777
  • 4
  • 73
  • 129
mike_b
  • 2,127
  • 5
  • 20
  • 25
  • 4
    That's exactly the problem solved by Dan Saks' ["Making New Friends" idiom](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Making_New_Friends). _(Sorry for the late comment.)_ – gx_ Sep 07 '13 at 15:18
  • I have linked to another question with an answer that goes into detail explaining *why* the suggested fixes are necessary/work – M.M Jun 05 '18 at 23:14

5 Answers5

42

It sounds like you want to change:

friend ostream& operator << (ostream& out, const Base<T>& e);

To:

template<class T>
friend ostream& operator << (ostream& out, const Base<T>& e);
asdfjklqwer
  • 3,536
  • 21
  • 19
  • 29
    So I didn't believe you, but this works. However, I can't use `T` because it `shadows` the already existing `T` of the class template. – mike_b Oct 28 '10 at 05:05
  • 3
    Does GCC give you a warning about the shadowing? – asdfjklqwer Oct 28 '10 at 05:06
  • 12
    Yes, GCC gave a warning about shadowing. I replaced T with Y and that resolved it. `error: shadows template parm ‘class T’` – mike_b Oct 28 '10 at 05:15
  • 3
    Interesting. I tested my answer with Visual Studio which doesn't have a problem with the shadowing. But I can see why GCC might spit out a warning... like you said, replacing T with another template parameter will eliminate the shadowing. Btw Ben, your solution doesn't compile in MSVC. – asdfjklqwer Oct 28 '10 at 05:22
  • Mine is also rejected by Comeau. Annoying because it's exactly the right syntax for what the OP wanted, but it apparently falls afoul of a restriction on friend functions. +1 to you. – Ben Voigt Oct 28 '10 at 05:45
  • 14
    So operator<<(ostream&, const Base&) becomes a friend of Base even when S and T are distinct? I think the answer should clarify this. – Tom Artiom Fiodorov Dec 30 '13 at 19:33
25

Gcc is rightly warning you. Despite it's appearances (it takes Base argument), it is not a function template.

Your class definition has a non-template declaration of the friend function (without the template), but the friend function definition later on is a function template (i.e. starts with template..).

Also your operator<< takes a Base *. This is not correct. It should be Base const & to retain it's built-in semantics

Probably you are looking at something as below:

template <typename T> 
class Base { 
  public: 
    friend ostream& operator << (ostream &out, Base<T> const &e){
       return out;
    }; 
}; 

int main(){
   Base<int> b;
   cout << b;
}

If you want fully templated, then this is probably what you want. But I am not sure how much useful this is over the previous one. Since the lookup involves ADL, you will never be able to resolve to any condition where T is not equal to U (as long as the call is from a context not related to this class e.g. from 'main' function)

template <typename T>  
class Base {  
  public:  
    template<class U> friend ostream& operator << (ostream &out, Base<U> const &e){ 
       return out; 
    };  
};

int main(){ 
   Base<int> b; 
   cout << b; 
} 
Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • Well, use of the `<<` operator wouldn't ever find any instantiation with `U != T`. A direct call potentially could though, which would just be really weird. It would only matter if `operator <<` for `Base` accessed a global variable of type `Base`. – Ben Voigt Oct 28 '10 at 05:48
  • @Ben Voigt: Added the part 'as long as the call is from a context not related to this class e.g. from 'main' function'. Hope it is clearer now – Chubsdad Oct 28 '10 at 05:58
  • Maybe add how to define the friend function outside the class definition, for completeness? – Gauthier Dec 14 '16 at 09:02
  • @Chubsdad The problem is you can't instantiate `Base` with two different template arguments like `Base b; Base c;` since it would make `template ostream& operator<<(ostream&, Base const &)` have multiple definitions (See here: ) – Artyer Aug 22 '22 at 17:30
9

Probably what you are looking for is:

template <typename T>
class Base;

template <typename T>
ostream& operator<< (ostream &, const Base<T>&);

template <typename T>
class Base
{
  public:
    template<>
    friend ostream& operator << <T>(ostream &, const Base<T> &);
};

template <typename T>
ostream& operator<< ( ostream &out, const Base<T>& e )
{
    return out << e->data;
}

This friends only a single instantiation of the template, the one where the operator's template parameter matches the class's template parameter.

UPDATE: Unfortunately, it's illegal. Both MSVC and Comeau reject it. Which raises the question of why the original error message suggested pretty much EXACTLY this approach.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 6
    The [C++ FAQ](http://www.parashift.com/c++-faq-lite/templates.html#faq-35.16) says that the `friend` line in the class declaration should have a `<>`, not a ``. – Michael Kristofik Dec 17 '10 at 19:44
  • 2
    And apparently you're not supposed to need the `template<>` line before it. (No access to compiler right now, haven't tried it.) – Michael Kristofik Dec 17 '10 at 19:51
  • I believe the original error message is suggesting `public: friend ostream& operator << <>(ostream &, const Base &);` – M.M Jun 05 '18 at 22:49
7

changing

friend ostream& operator << (ostream& out, const Base<T>& e);

to

friend ostream& operator << <T>(ostream& out, const Base<T>& e);

should work as well--I just solved an identical problem in this way.

cognalog
  • 440
  • 5
  • 8
0

changing

friend ostream& operator << (ostream &out, Base<T> *e)`

To

template<T> friend ostream& operator << (ostream &out, Base *e)
  • This is a fake solution. `template ` is in fact `template ` in which an unnamed non-type parameter is declared. This makes the call to this function fail because the template parameter is not provided, and therefore lookup will select the global version of `operator<<`. But this does not declare the global one as a friend of `Base`, so access of `Base`'s private members in that function is not permitted. – GKxx May 19 '23 at 21:34
  • In fact, this has the same effect as removing that friend declaration. – GKxx May 19 '23 at 21:35