Проблема заключается в том, что компилятор не пытается использовать шаблонный operator<<
, который вы предоставили, а скорее не шаблонную версию.
Когда вы объявляете друга внутри класса, вы вводите декларацию этой функции в охватывающую область. Следующий код имеет эффект декларирования (и не определяет) свободную функцию, которая принимает non_template_test
аргумент постоянной ссылки:
class non_template_test
{
friend void f(non_template_test const &);
};
// declares here:
// void f(non_template_test const &);
То же самое происходит с шаблонных классов, даже если в данном случае это немного менее интуитивным , Когда вы объявляете (а не определяете) функцию друга внутри тела шаблона, вы объявляете свободную функцию с точными аргументами.Обратите внимание, что вы объявляете функцию, а не функцию шаблона:
template<typename T>
class template_test
{
friend void f(template_test<T> const & t);
};
// for each instantiating type T (int, double...) declares:
// void f(template_test<int> const &);
// void f(template_test<double> const &);
int main() {
template_test<int> t1;
template_test<double> t2;
}
Эти свободные функции объявлены, но не определены. Трудная часть здесь заключается в том, что эти свободные функции не являются шаблоном, а объявляются обычные свободные функции. При добавлении функции шаблона в смесь вы получите:
template<typename T> class template_test {
friend void f(template_test<T> const &);
};
// when instantiated with int, implicitly declares:
// void f(template_test<int> const &);
template <typename T>
void f(template_test<T> const & x) {} // 1
int main() {
template_test<int> t1;
f(t1);
}
Когда компилятор попадет в основную функции она инстанцирует шаблон template_test
с типом int
и объявляет свободную функцию void f(template_test<int> const &)
, не шаблонная. Когда он находит вызов f(t1)
, есть два символа f
, которые соответствуют: не-шаблон f(template_test<int> const &)
объявлен (и не определен), когда был создан экземпляр template_test
, и шаблонная версия, которая объявлена и определена как 1
. Не templated версия имеет приоритет, и компилятор соответствует ему.
Когда компоновщик пытается разрешить не templated версию f
, он не может найти символ, и он таким образом не работает.
Что мы можем сделать? Существует два разных решения. В первом случае мы создаем компилятор для нестандартных функций для каждого типа экземпляра. Во втором случае мы объявляем шаблонную версию как друга. Они немного отличаются друг от друга, но в большинстве случаев эквивалентны.
Имея компилятор генерировать не-шаблонные функции для нас:
template <typename T>
class test
{
friend void f(test<T> const &) {}
};
// implicitly
Это имеет эффект создания, как много не-шаблонные свободные функций по мере необходимости. Когда компилятор находит объявление друга в шаблоне test
, он не только находит объявление, но и реализует его и добавляет как к охватывающей области.
Создания шаблонной версии друга
Чтобы сделать шаблон друг, мы должны это уже объявлено и сообщить компилятору, что друг мы хотим на самом деле шаблон, а не не-шаблонная свободная функция:
template <typename T> class test; // forward declare the template class
template <typename T> void f(test<T> const&); // forward declare the template
template <typename T>
class test {
friend void f<>(test<T> const&); // declare f<T>(test<T> const &) a friend
};
template <typename T>
void f(test<T> const &) {}
в этом случае, до определения f
в качестве шаблона, мы должны перенаправлять объявить шаблон. Чтобы объявить шаблон f
, мы должны сначала отправить объявление test
. Объявление друга изменено, чтобы включить угловые скобки, которые идентифицируют, что элемент, который мы создаем другом, на самом деле является шаблоном, а не свободной функцией.
Вернуться к проблеме
Возвращаясь к конкретному примеру, самое простое решение, имеющий компилятору генерировать функции для вас встраивания декларации функции друга:
template <typename T>
class BinaryTree {
friend std::ostream& operator<<(std::ostream& o, BinaryTree const & t) {
t.dump(o);
return o;
}
void dump(std::ostream& o) const;
};
С этот код вы вынуждаете компилятор генерировать не templated operator<<
для каждого типа экземпляра, и что сгенерированные функции делегаты в методе dump
шаблона.
Какой тип 'tree' указан в файле saveToFile? –
Используете ли вы пространства имен в своем коде (в частности, при объявлении 'BinaryTree' и вашей перегрузке 'operator <<')? Если да, пожалуйста, покажите их в контексте. –
Просьба указать точный текст сообщения об ошибке, предоставленный компилятором, в полном объеме. –