c++ faq 35.16
http://www.parashift.com/c++-faq-lite/template-friends.html
#include <iostream>
template<typename T>
class Foo {
public:
Foo(T const& value = T());
friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x);
private:
T value_;
};
The autor claims:
‘The snag happens when the compiler sees the friend lines way up in the class definition proper. At that moment it does not yet know the friend functions are themselves templates (why is it? aren’t class template member functions be function template by default?); it assumes they are non-templates like this:’
Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs)
{ ... }
std::ostream& operator<< (std::ostream& o, const Foo<int>& x)
{ ... }
Why are the above non-templates? aren’t these templates that are instantiated via int?
‘When you call the operator+ or operator<< functions, this assumption causes the compiler to generate a call to the non-template functions, but the linker will give you an “undefined external” error because you never actually defined those non-template functions. ‘
In fact, to make compiler recognize the above as function template, programmer has to do this explicitly like below:
template<typename T> class Foo; // pre-declare the template class itself
template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x);
Could anyone explain? I find this quite vexing and do not know why compiler does not just instantiate a instance of Class Foo by replacing T with ‘int’, and call it a day.
Thanks.
Class template member functions are part of the template and are therefore instantiated with the template, but friends are not. Consider the non-template case:
Note that
void foo(S)does not have to be declared at this point; thefrienddeclaration is saying that if a functionvoid foo(S)is defined, then that function will have access toS. It might never actually be defined, and that’s fine.With templates, the situation is the same:
This is saying that for any type
T, if a functionvoid foo(S<T>)is defined then that function has access toS<T>. That function is expected to be a concrete function, by overloading:The compiler doesn’t know that you are planning later on to supply a function template that can be used for all
T. Instead, if an appropriate function template is already declared then it will be instantiated if you specify that it should by adding angle brackets.As for why you have to forward-declare the template, there’s no reason that “the template” has to have just one declaration. Consider:
Here there are two specialisations of
foo, and they have to both be forward declared so that thefriendcan select between them.