Reputation: 2278
Given the following code.
#include <iostream>
template<typename T>
class Foo
{
public:
Foo(const T& 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 compiler has no trouble compiling both friend functions which have template parameters without having the following syntax
template <typename T>
friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)
or
friend Foo<T> operator+ <>(const Foo<T>& lhs, const Foo<T>& rhs)
or
friend Foo<T> operator+ <T>(const Foo<T>& lhs, const Foo<T>& rhs)
because they have been defined by their implementation inside the template class itself.
How can the compiler compile these friend functions with their template parameters without including a template declaration? Why is just having their implementation inside of the class enough?
I learned about this concept from here under the section on "Why do I get linker errors when I use template friends?"
Upvotes: 0
Views: 240
Reputation: 275330
Those two options, with and without template<class T>
, do slightly different things.
When you introduce a friend
function that way, you introduce it within the enclosing namespace in a way that is only reachable via ADL (argument dependent lookup).
The template<class T>
introduces a function template, and the one without introduces an actual function.
So this:
template<class T>
struct foo {
friend void bar(foo<T>){}
};
means that when foo<int>
exists, a function bar(foo<int>)
is created. Then foo<double>
creates bar(foo<double>)
.
Each of these bar
s are not template
functions. They are eaxh a function with a fixed signature, a new overload, imilar to if you wrote
void bar(foo<char>){}
right after foo
. The exception is that the friend bar
can only be found via ADL, which changes how conflicts and overload resolution works.
Now this:
template<class T>
struct foo {
template <typename X>
friend void bar(foo<X>){}
};
creates a template
bar
for each instance of foo
. These do not conflict, because they are only found via ADL. And the only one that can be found is the one where the T
matches the X
(in this case -- with more arguments it could differ).
It is rarely a good idea to do the template
version, in my experience.
Upvotes: 2