Почему эта функция шаблона не компилируется?

Это довольно короткий фрагмент, который просто не скомпилируется с 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
reference to
std :: basic_ostream>&
оператор<< (Foo&, char const (&) [4]) ‘collect2:
ld вернул 1 статус выхода

Кто-нибудь может объяснить, почему?

РЕДАКТИРОВАТЬ

Я также попробовал с clang 3.1, и он говорит точно то же самое.

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 здесь не связаны), и соответствует вашему определению на уровне пространства имен.

Другой вариант — определение функции друга внутри определения шаблона класса, в этом случае вы можете сохранить исходную подпись. Для получения дополнительной информации о различных альтернативах, посмотрите на это другое ответ

8

Другие решения

Вы на самом деле не написали оператор вывода<< для Фу

Обратите внимание, что подписи для этих двух функций очень разные

1

По вопросам рекламы [email protected]