|
||||
|
Section 35:
|
[35.16] Why do I get linker errors when I use template friends?
Ah, the intricacies of template friends. Here's an example of what people often want to do:
#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_;
};
Naturally the template will need to actually be used somewhere:
int main()
{
Foo<int> lhs(1);
Foo<int> rhs(2);
Foo<int> result = lhs + rhs;
std::cout << result;
...
}
And of course the various member and friend functions will need to be defined
somewhere:
template<typename T>
Foo<T>::Foo(T const& value = T())
: value_(value)
{ }
template<typename T>
Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)
{ return Foo<T>(lhs.value_ + rhs.value_); }
template<typename T>
std::ostream& operator<< (std::ostream& o, const Foo<T>& x)
{ return o << x.value_; }
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; 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)
{ ... }
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.
The solution is to convince the compiler while it is examining the class body proper that the operator+ and operator<< functions are themselves templates. There are several ways to do this; one simple approach is pre-declare each template friend function above the definition of template class Foo: 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);Also you add <> in the friend lines, as shown:
#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_;
};
After the compiler sees that magic stuff, it will be better informed about the
friend functions. In particular, it will realize that the
friend lines are referring to functions that are themselves templates.
That eliminates the confusion.
Another approach is to define the friend function within the class body at the same moment you declare it to be a friend. For example:
#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_;
};
|
|||