2009-11-27 4 views
5

Я пытаюсь реализовать метод для двоичного дерева, которое возвращает поток. Я хочу, чтобы использовать поток, возвращаемый в методе, чтобы показать дерево на экране или сохранить дерево в файле:Оператор перегрузки << для шаблонного класса

Этих двух метода в классе бинарного дерева:

декларациях:

void streamIND(ostream&,const BinaryTree<T>*); 
friend ostream& operator<<(ostream&,const BinaryTree<T>&); 

template <class T> 
ostream& operator<<(ostream& os,const BinaryTree<T>& tree) { 
    streamIND(os,tree.root); 
    return os; 
} 

template <class T> 
void streamIND(ostream& os,Node<T> *nb) { 
    if (!nb) return; 
    if (nb->getLeft()) streamIND(nb->getLeft()); 
    os << nb->getValue() << " "; 
    if (nb->getRight()) streamIND(nb->getRight()); 
} 

Этот метод в классе UsingTree:

void UsingTree::saveToFile(char* file = "table") { 
    ofstream f; 
    f.open(file,ios::out); 
    f << tree; 
    f.close(); 
} 

Так что я перегружен оператор "< <" класса BinaryTree использовать: соиЬ < < дерева и ofstream ф < < дерева, но я получаю следующее сообщение об ошибке: неопределенная ссылка на `оператора < < (станд :: basic_ostream> &, BinaryTree &)»

P.S. Дерево хранит объекты Word (строка с int).

Надеюсь, вы понимаете мой плохой английский. Спасибо! И я хотел бы знать хороший текст для новичков о STL, который объясняет все необходимое, потому что я трачу все свое время на ошибки, подобные этому.

EDIT: дерево в saveToFile() объявлено: BinaryTree < Слово> дерево.

+0

Какой тип 'tree' указан в файле saveToFile? –

+0

Используете ли вы пространства имен в своем коде (в частности, при объявлении 'BinaryTree ' и вашей перегрузке 'operator <<')? Если да, пожалуйста, покажите их в контексте. –

+0

Просьба указать точный текст сообщения об ошибке, предоставленный компилятором, в полном объеме. –

ответ

8

Проблема заключается в том, что компилятор не пытается использовать шаблонный 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 шаблона.

+0

Я думаю, что этот меня многому научил. Благодаря! – peterJk

+0

+1 Я всегда хотел иметь эту информацию в одном удобном месте. –

+0

+1 Решил мою проблему :). – Yuri

2

Убедитесь, что полные определения шаблонов (а не только прототипы) находятся в файле include (i .h, .hpp), поскольку шаблоны и отдельная компиляция не работают вместе.

Я не знаю, что использует линкер @Dribeas, но это может привести к тому, что компоновщик GNU даст неопределенную опорную ошибку.

+1

Не имеет отношения к проблеме: хотя это хороший общий совет, он не решает проблему, почему компоновщик не находит функцию. –

+1

На самом деле это даст ошибку компоновщика, но не с кодом в вопросе. В коде вопроса функция, которая не находится, не является шаблоном, а не шаблонной свободной функцией. Обратите внимание, что шаблонный 'operator <<' не объявлен заранее, просто определен. Это означает, что либо компилятор использует его, и символ определен для используемого компоновщика, или, как это бывает, компилятор даже не пытается сопоставить с шаблоном 'operator <<' и где шаблон определен не имеет отношения к проблеме. –

3

вам не нужно заявление оператора шаблона и вы должны объявить оператор «друг» для вашего класса, чтобы предоставлен доступа к другим классам, в данном случае станде :: соиЬ

friend std::ostream& operator << (std::ostream& os, BinaryTree & tree) 
{ 
    doStuff(os, tree); 
    return os; 
}

порекомендовано чтение : http://www.parashift.com/c++-faq-lite/friends.html

+0

@Charles: Вы правы, но кажется, что двоичное дерево хранит только объекты Word. –

+0

Не обращайте внимания на мой предыдущий комментарий ... вам не нужен параметр шаблона, если оператор '<<' определен как встроенная функция друга. –

+0

+1, даже если это не объясняет, почему, или тот факт, что он может быть более явным, поскольку определение должно находиться в определении BinaryTree. Но ведь это решение. –

3

При перегрузке << оператора вы хотите использовать константную ссылку:

template <class T> 
std::ostream& operator << (std::ostream& os, const BinaryTree<T>& tree) 
{ 
    // output member variables here... (you may need to make 
    // this a friend function if you want to access private 
    // member variables... 

    return os; 
} 
+2

Не имеет отношения к проблеме: это не имеет никакого отношения к тому, что компоновщик не найдет определение функции. –