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.
Member functions can be function templates in several contexts. All functions of class templates are generic but are not referred to as member templates or member function templates. If these member functions take their own template arguments, they are considered to be member function templates.
Friend functions aren't considered class members; they're normal external functions that are given special access privileges. Friends aren't in the class's scope, and they aren't called using the member-selection operators (. and ->) unless they're members of another class.
Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.
Defining a Function TemplateA function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
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:
struct S {
friend void foo(S);
};
Note that void foo(S)
does not have to be declared at this point; the friend
declaration is saying that if a function void foo(S)
is defined, then that function will have access to S
. It might never actually be defined, and that's fine.
With templates, the situation is the same:
template<typename T> struct S {
friend void foo(S);
};
This is saying that for any type T
, if a function void foo(S<T>)
is defined then that function has access to S<T>
. That function is expected to be a concrete function, by overloading:
void foo(S<char>) { }
void foo(S<int>) { }
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:
#include <iostream>
template<typename T> struct S;
template<typename T> void foo(S<T>);
template<typename T> void foo(S<T *>);
template<typename T> struct S {
friend void foo<>(S);
};
template<typename T> void foo(S<T>) { std::cout << "template template friend\n"; }
template<typename T> void foo(S<T *>) { std::cout << "template specialization template friend\n"; }
template void foo(S<void *>);
int main() {
foo(S<int>());
foo(S<void *>());
}
Here there are two specialisations of foo
, and they have to both be forward declared so that the friend
can select between them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With