How do you declare a friend of a generic class outside of a class declaration?

The following code works, but I would like to move the ostream & <<operator outside of the declearation class, as I did with hash :: operator [].

#include<iostream>
#include<map>

using namespace std;

template <class T>
class hash {
  private:
    map<string, T> _map;
  public:
    T& operator[] (string x);
    friend ostream& operator<<(ostream& out, const hash<T> &rhs) { return out << "test"; }
};

template <class T>
T & hash<T>::operator[](string x) {
  return _map[x];
}

int main () {
  hash<int> myobject;
  myobject["a"] = 1;
  cout << myobject["a"] << endl;
  cout << myobject << endl;
  return 0;
}

      

I tried:

template <class T>
ostream& operator<<(...) {
  return out << "test";
}

      

and

ostream& operator<<(...) {
  return out << "test";
}

      

as well as several other combinations to no avail.

+3


source to share


2 answers


You must be careful when using a friend and a template

#include<iostream>
#include<map>

template <class T>
class MyHash{
public:
    // ... use your T template here
    template <class U>
    friend ostream& operator<<(ostream& out, const MyHash<U> &rhs);
};

// ...
template <class U>
ostream& operator<<(ostream& out, const MyHash<U> &rhs){ return out << "test"; }

      

you need to use a different U template because operator << is a friend's (external) method , if we use T instead of U we have: undefined definition in the template .

Change the hash to MyHash to avoid ambiguity.

Edit:



The error you are getting here :

error C2676: '<' binaire: 'std :: string' ne définit pas cet opérateur ou une conversion vers un type acceptable pour l'opérateur prédéfini

because you forgot to include <string>

in "hash.h" . This header defines the <operator .

And try moving the definition of operator [] and operator < directly to "hash.h" Both the declaration and the template definition should be included. This is because in some compilers, template functions cannot be compiled and linked independently as they are generated on demand for the specific types with which they are created.

Change #include "hash.cpp" to #include "hash.h"

+3


source


Since this question doesn't seem to close as an exact duplicate, I'll explain what your program is doing.

template <typename T>
class test {
   int private_field;
   friend std::ostream& operator<<( std::ostream&, test<T> const & );
};
// ???
int main() {
   std::cout << test<int>() << std::endl;
}

      

Templates are created on demand (unless you explicitly create them), which means that this particular program test

only creates as test<int>

. When the compiler instantiates a template (because it was requested in main

), it will process the template definition. At this point, the behavior is similar to rewriting the overridden code at the point of definition (in this case, right before main

):

class test<int> {
   friend std::ostream& operator<<( std::ostream&, test<int> const & );
};

      

Now, if you look at the template instance, you can see that the declaration friend

supports a friendly function. So in this particular program, you can fill in with ???

this particular function:

std::ostream& operator<<( std::ostream& o, test<int> const & t ) {
   return o << t.private_field;
}

      

The problem is that it is not easy to expand. The code for this is operator<<

the same for test<int>

as for test<double>

, so you don't need to rewrite the same function for all instance types!



At this point, there are two options: the first one, as you have already defined, provides a function definition within a class. The compiler then processes and defines the function whenever the type is instantiated. This solution creates non-standard functions on demand for each instance of the template (this is an option I would do, even with the quirks and weirdness of searching here).

Now, if you really want to avoid defining a templating class inside the class, and still want to provide a single implementation, you need to resort to templating operator<<

. At this point, you have two different options: you can declare all instances of the template (which I don't really like because it opens up to too many others), or you can befriend one specialization of the template function (cleaner access to it, more cumbersome to write ).

First case:

template <typename T>
class test {
   template <typename U>
   friend std::ostream& operator<<( std::ostream&, test<U> const & );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

      

The second case requires several advanced declarations:

template <typename T> test;
template <typename T> std::ostream& operator<<( std::ostream&, test<T> const & );
template <typename T>
class test {
   friend std::ostream& operator<< <T>( std::ostream&, const test<T>& );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

      

There is, of course, another option: don't declare at all friend

. Provide a public function print(std::ostream&)

in your class that has an implementation, and provide a non templated operator<<

one that just calls print

on the second argument.

+6


source







All Articles