84

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong... The overloaded << works if I make it into an inline function. But how do I make it work in my case?

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

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

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

The code:

template <class T>
T my_max(T a, T b)
{
   if(a > b)      
      return a;
   else
      return b;
}

template <class classT>
class D
{
public:
   D(classT in)
      : d(in) {};
   bool operator>(const D& rhs) const;
   classT operator=(const D<classT>& rhs);

   friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
   classT d;
};


int main()
{

   int i1 = 1;
   int i2 = 2;
   D<int> d1(i1);
   D<int> d2(i2);

   cout << my_max(d1,d2) << endl;
   return 0;
}

template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
   os << rhs.d;
   return os;
}
Eric Schmidt
  • 312
  • 1
  • 11
starcorn
  • 8,261
  • 23
  • 83
  • 124
  • There was a recent question about this which may be instructive: http://stackoverflow.com/questions/4571611/virtual-operator/ – Daniel Trebbien Jan 11 '11 at 16:52
  • @Daniel - it doesn't takes up the problem I have when overloading for a template class – starcorn Jan 11 '11 at 16:55
  • 9
    I think it is better if you do not modify the question with a given answer. It makes it harder to determine what the original problem was. You might want to add an **EDIT** at the end with the change that *solved* the issue, but I find it confusing when questions change overtime and I have to pull up the history to see what was actually being asked in the first place. – David Rodríguez - dribeas Jan 11 '11 at 19:13

5 Answers5

210

This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.

The extrovert

Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.

template <typename T>
class Test {
   template <typename U>      // all instantiations of this template are my friends
   friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
   // Can access all Test<int>, Test<double>... regardless of what T is
}

The introverts

Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).

This can be done in two ways, the simple way being as @Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:

template <typename T>
class Test {
   friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
      // can access the enclosing Test. If T is int, it cannot access Test<double>
   }
};

In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.

The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:

// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );

// Declare the actual templates:
template <typename T>
class Test {
   friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
   // Can only access Test<T> for the same T as is instantiating, that is:
   // if T is int, this template cannot access Test<double>, Test<char> ...
}

Taking advantage of the extrovert

The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:

namespace hacker {
   struct unique {}; // Create a new unique type to avoid breaking ODR
   template <> 
   std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
   {
      // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
      // if Test<T> is an introvert, then I can only mess up with Test<unique> 
      // which is just not so much fun...
   }
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    Implementing the function outside of the class gives a undefined reference. Inside of the class I get a problem as soon as I explicitely instanciate the host class with more than one type. – dgrat May 12 '16 at 17:43
  • 1
    @dgrat: I cannot debug code that I am not seeing, but it does work. Is the friend dependent on any of the template arguments? If it is not, you would get issues with multiple definitions as different instantiations would generate exactly the *same* function (or an ODR violation if the function signature is the same but the body differs). – David Rodríguez - dribeas May 13 '16 at 14:36
  • This continues to be one of my more referred go-to's for people seeking to friend template functions (not just operators) due in no small part to the distinct differences in ways i can be done, and the benefits/detractions for each. Just a stellar answer, David. I'd uptick it a dozen times over if I could. – WhozCraig Aug 15 '17 at 19:08
  • In third version is "operator<< " this line means instantiate the function template of operator << as a friend of class template Test with the same T ? – wangdq Aug 05 '18 at 03:30
  • @wangdq Yes, and that specialization is friended to, and *only* to, that friended-by template specialization. I.e. If you have some `Test` the only `operator <<` friended to that is the `operator << ` instantiation. Some arbitrary `Test`, where `U` is not synonymous with `T`, does not friend `operator << `; it friends `operator << ` instead. It may not seem a big deal, but it can quietly become one without realizing it. – WhozCraig Aug 22 '18 at 16:11
  • @DavidRodríguez-dribeas well studied, but I am not entirely clear how I would solve the following in the "Introvert" style : https://wandbox.org/permlink/jMKpn6CKFL2cyceO ... It seems if I like to preserve the full encapsulation of nested type names I can not use the simple operator overloading on them. –  Dec 06 '18 at 11:14
  • In the third example, what's the purpose of the template declaration *inside* the class? Isn't the first forward declaration enough? – Clément Feb 26 '20 at 06:25
  • @DavidRodríguez-dribeas, what can I do with first idea for introverts, if code implementation not in header file? – Evgeny Oct 30 '20 at 11:56
16

You can't declare a friend like that, you need to specify a different template type for it.

template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);

note SclassT so that it doesn't shadow classT. When defining

template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
  // body..
}
Nim
  • 33,299
  • 2
  • 62
  • 101
  • thanks this works edited my question with this code, I will tick this as answer as soon the ticker goes down. – starcorn Jan 11 '11 at 17:00
  • 4
    Note that this is not declaring a particular instantiation of the `operator<<` as a friend, but rather **all** instantiations, including any specialization of the template. See the answer [here](http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372) – David Rodríguez - dribeas Jan 11 '11 at 19:11
  • @starcorn you should change your selected answer in order to provide better answer and that should be David Rodriguez's answer. – Rupesh Yadav. Apr 08 '16 at 11:42
  • @Nim based on user opinion I changed the selected answer for my question. However thanks for your contribution to help me at that time :) – starcorn Apr 08 '16 at 11:44
  • @starcorn, no worries, I voted for David's answer as well way back then, it's a more comprehensive treatment of the problem.. – Nim Apr 11 '16 at 08:07
  • You can ensure that `classT` and `SclassT` are the same with `std::enable_if` and `std::is_same` on any of the types in the declaration, e.g. `template typename std::enable_if::value, ostream>::type& operator<< (ostream & os, const D& rhs);` – ulatekh Sep 22 '21 at 23:31
4

This worked for me without any compiler warnings.

#include <iostream>
using namespace std;

template <class T>
T my_max(T a, T b)
{
  if(a > b)
    return a;
  else
    return b;
}

template <class classT>
class D
{
public:
  D(classT in)
    : d(in) {};

  bool operator>(const D& rhs) const {
    return (d > rhs.d);
  }

  classT operator=(const D<classT>& rhs);

  friend ostream& operator<< (ostream & os, const D& rhs) {
    os << rhs.d;
    return os;
  }

private:
  classT d;
};


int main()
{

  int i1 = 1;
  int i2 = 2;
  D<int> d1(i1);
  D<int> d2(i2);

  cout << my_max(d1,d2) << endl;
  return 0;
}
EmeryBerger
  • 3,897
  • 18
  • 29
  • Yes i already did that, but what about if I don't want the `operator<<` as an inline function? – starcorn Jan 11 '11 at 16:56
  • @starcorn: Wheather a method/function is taged inline (implicitly or explicitly) has little to do weather the function is actually inlined in the code. Therefore this is a meaningless worry. – Martin York Jan 11 '11 at 18:33
  • +1. @starcorn: This solution is better than the accepted one. The differce is subtle, but in the accepted answer, you are declaring all instantiations (and possible specializations) of the `operator<<` as friends, while in this solution you are only granting access to the instantiation of `operator<<` that has the same type. Also, as a side effect of defining `operator<<` inside the class you are limiting the visibility of that `operator<<` to only those cases where one of the two arguments is a `D` --the compiler will not even consider the `operator<<` overload unless one argument is `D`. – David Rodríguez - dribeas Jan 11 '11 at 18:50
  • 2
    @starcorn: I have added an answer that tries to clear the differences in three different approaches [here](http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372) – David Rodríguez - dribeas Jan 11 '11 at 18:59
1

I think you shouldn't make friend in the first place.

You can create a public method call print, something like this (for a non template class):

std::ostream& MyClass::print(std::ostream& os) const
{
  os << "Private One" << privateOne_ << endl;
  os << "Private Two" << privateTwo_ << endl;
  os.flush();
  return os;
}

and then, outside the class (but in the same namespace)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
  return myClass.print(os);
}

I think it should work also for template class, but I haven't tested yet.

Alessandro Teruzzi
  • 3,918
  • 1
  • 27
  • 41
0

Here you go:

#include <cstdlib>
#include <iostream>
using namespace std;

template <class T>
T my_max(T a, T b)
{
   if(a > b)      
      return a;
   else
      return b;
}

template <class classT>
class D
{
public:
   D(classT in)
      : d(in) {};
   bool operator>(const D& rhs) const { return d > rhs.d;};
   classT operator=(const D<classT>& rhs);

   template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
   classT d;
};

template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs)
{
    os << rhs.d;
    return os;
}


int main()
{

   int i1 = 1;
   int i2 = 2;
   D<int> d1(i1);
   D<int> d2(i2);

   cout << my_max(d1,d2) << endl;
   return 0;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • I don't think this should compile: `template ostream& operator<<(ostream& os, class D const& rhs)`. Elaborated type is not allowed in parameter declaration and `typename` requires qualified-id. – Gene Bushuyev Jan 11 '11 at 17:49
  • @Gene: hmm. it does compile for me on highest levels with MS extensions turned off. – John Dibling Jan 11 '11 at 17:54
  • It does not compile with g++, and I trust the compiler in this one. The second argument in the `operator<<` is `class D`, and I think that is incorrect. I would use `D` instead. The keyword `class` is optional there (99.9% of the cases), but the use of `typename` is not one of the two know uses: it would be invalid as a substitute to `class`, and it is to identifying that a dependent name on a template is actually a type. – David Rodríguez - dribeas Jan 11 '11 at 19:08