Why doesn't that template function compile?
-
30-06-2021 - |
Question
This is a pretty short snippet that just won’t compile with g++ 4.7.1 (it won’t compile either with gcc 4.6.3 by the way).
#include <iostream>
template<typename T>
struct Foo
{
template<typename U>
friend std::ostream& operator<<(Foo&, U&);
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u)
{
std::cout << u;
return std::cout;
}
int main()
{
Foo<int> f;
f << "bar";
return 0;
}
And this is what gcc 4.7.1 outputs (4.6.3 says almost the same thing).
/tmp/ccNWJW6X.o: In function
main': main.cpp:(.text+0x15): undefined reference to
std::basic_ostream >& operator<< (Foo&, char const (&) [4])' collect2: ld returned 1 exit status
Anyone could explain why?
EDIT
I also tried with clang 3.1, and it says exactly the same thing.
Solution
Friendship with templates can be a bit complicated... Lets see what your code does:
template<typename T>
struct Foo {
template<typename U>
friend std::ostream& operator<<(Foo&, U&); // [1]
};
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u) { // [2]
std::cout << u;
return std::cout;
}
When you instantiate Foo
with a type, for example int
the friend declaration in [1] declares a template function:
template <typename U>
std::ostream& operator<<(Foo<int>&,U&);
But that function does not exist anywhere, what you are providing in [2] is a template that takes two arguments:
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u);
The key point is that the friend declaration is processed while the template is being instantiated, and at that time Foo
represents the type obtained with the current instantiation.
There are different options for what you want to do, the simplest is changing the friend declaration to:
template<typename W, typename U>
friend std::ostream& operator<<(Foo<W> foo, U& u);
Which declares a template taking two arguments (both W
and U
are unbound here), and matches your definition at namespace level.
Another option is defining the friend function inside the class template definition, in which case you can maintain the original signature. For more information on the different alternatives, take a look at this other answer
OTHER TIPS
You did not actually write the output operator<< for Foo
Notice the signatures for the two functions are very different