Вопрос

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

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

Заявления:

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 для использования:cout << дерево и ofstream f << дерево, но я получаю следующее сообщение об ошибке:неопределенная ссылка на `оператор<<(std::basic_ostream >&, двоичное дерево&)'

P.S.В дереве хранятся объекты Word (строка с значением int).

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

Редактировать:дерево в SaveToFile() объявлено:Бинарное дерево< Слово > дерево.

Это было полезно?

Решение

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

Когда компоновщик пытается разрешить не шаблонную версию f он не может найти символ и, таким образом, терпит неудачу.

Что мы можем сделать?Есть два разных решения.В первом случае мы заставляем компилятор предоставлять не шаблонные функции для каждого создаваемого типа.Во втором случае мы объявляем шаблонную версию как дружественную.Они слегка отличаются друг от друга, но в большинстве случаев эквивалентны.

Заставить компилятор генерировать для нас не шаблонные функции:

template <typename T>
class test 
{
   friend void f( test<T> const & ) {}
};
// implicitly

Это приводит к созданию такого количества свободных функций без шаблонов, сколько необходимо.Когда компилятор находит объявление friend в шаблоне 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 шаблон.Объявление друга изменено, чтобы включить угловые скобки, которые указывают на то, что элемент, который мы делаем другом, на самом деле является шаблоном, а не бесплатной функцией.

Вернемся к проблеме

Возвращаясь к вашему конкретному примеру, самым простым решением является то, что компилятор генерирует функции для вас путем встраивания объявления функции friend:

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;
};

С помощью этого кода вы заставляете компилятор генерировать не шаблонный operator<< для каждого созданного типа, и эта сгенерированная функция делегирует на dump метод создания шаблона.

Другие советы

вам не нужно объявление оператора шаблона, и вы должны объявить оператор "friend", чтобы вашему классу был предоставлен доступ к другим классам, в данном случае std::cout

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

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

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

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;
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top