Since it does not seem that this question is getting closed as exact duplicate, I will explain what your program does.
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 instantiated on demand (unless you explicitly instantiate them), that means that in this particular program test
is instantiated only as test<int>
. When the compiler instantiates the template (because it was requested in main
) it will process the template definition. At this point the behavior is similar to rewriting the code with the substituted type 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 instantiated template, you can note that the friend
declaration is befriending a non templated function. So in this particular program you can fill in the ???
with that particular function:
std::ostream& operator<<( std::ostream& o, test<int> const & t ) {
return o << t.private_field;
}
The problem here is that this is not easily extensible. The code of that operator<<
is the same for test<int>
than it is for test<double>
, so there should be no need to rewrite the same function for all instantiating types!
At this point there are two options, the first one is, as you have already identified, providing the definition of the function inside the class. The compiler will then process and define the function whenever the type is being instantiated. This solution creates non-templated functions on demand for each template instantiation (which is the option I would do, even with the quirks and strangeness of lookup here).
Now, if you really want to avoid providing the definition inside the templates class, and you still want to provide a single implementation, then you have to resort to providing a templates operator<<
. At this point you have two different options, you can declare all instantiations of the template (which I don't quite like, as it opens to too many others), or you can befriend a single specialization of the template function (cleaner regarding access, more cumbersome to write).
The first case is:
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 a couple of forward 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: do not declare friend
s at all. Provide a print(std::ostream&)
public function in your class that has the implementation, and provide a non-templated operator<<
that just calls print
on the second argument.