Это довольно короткий фрагмент, который просто не скомпилируется с g ++ 4.7.1 (кстати, он не скомпилируется ни с gcc 4.6.3).
#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;
}
И это то, что выводит gcc 4.7.1 (4.6.3 говорит почти то же самое).
/tmp/ccNWJW6X.o: в функции
main': main.cpp:(.text+0x15): undefined
std :: basic_ostream>&
reference to
оператор<< (Foo&, char const (&) [4]) ‘collect2:
ld вернул 1 статус выхода
Кто-нибудь может объяснить, почему?
РЕДАКТИРОВАТЬ
Я также попробовал с clang 3.1, и он говорит точно то же самое.
Дружба с шаблонами может быть немного сложнее … Давайте посмотрим, что делает ваш код:
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;
}
Когда вы создаете экземпляр Foo
с типом, например int
объявление друга в [1] объявляет функцию шаблона:
template <typename U>
std::ostream& operator<<(Foo<int>&,U&);
Но эта функция нигде не существует, то, что вы предоставляете в [2] — это шаблон, который принимает два аргумента:
template<typename T, typename U>
std::ostream& operator<<(Foo<T> foo, U& u);
Ключевым моментом является то, что объявление друга обрабатывается во время создания шаблона, и в то время Foo
представляет тип, полученный с помощью текущего экземпляра.
Существуют различные варианты того, что вы хотите сделать, самое простое — изменить объявление друга на:
template<typename W, typename U>
friend std::ostream& operator<<(Foo<W> foo, U& u);
Который объявляет шаблон с двумя аргументами (оба W
а также U
здесь не связаны), и соответствует вашему определению на уровне пространства имен.
Другой вариант — определение функции друга внутри определения шаблона класса, в этом случае вы можете сохранить исходную подпись. Для получения дополнительной информации о различных альтернативах, посмотрите на это другое ответ
Вы на самом деле не написали оператор вывода<< для Фу
Обратите внимание, что подписи для этих двух функций очень разные