Pergunta

Eu estou tentando implementar um método para uma árvore binária que retorna um córrego. Eu quero usar o fluxo retornado em um método para mostrar a árvore na tela ou para salvar a árvore em um arquivo:

Estes dois métodos são na classe da árvore binária:

As declarações:

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

Este método é na classe UsingTree:

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

Então eu sobrecarregado o operador "<<" da classe BinaryTree ao uso: cout << árvore e ofstream f << árvore, mas eu recebo o seguinte mensagem de erro: undefined reference to `operador << (std :: basic_ostream > &, BinaryTree &) '

P.S. A árvore armazena Palavra objetos (uma string com um int).

Espero que você entenda o meu pobre Inglês. Obrigado! E eu gostaria de saber um bom texto para iniciantes sobre STL que explica tudo necessário porque eu perco todo o meu tempo em erros como este.

EDIT: árvore no SaveToFile () é declarado:. BinaryTree árvore

Foi útil?

Solução

O problema é que o compilador não está tentando usar o operator<< templated que você forneceu, mas sim uma versão não-templated.

Quando você declarar um amigo dentro de uma classe que você está injetando a declaração de que a função no escopo delimitador. O seguinte código tem o efeito de declarar (e não definindo) uma função livre que leva um argumento non_template_test por referência constante:

class non_template_test
{
   friend void f( non_template_test const & );
};
// declares here:
// void f( non_template_test const & ); 

O mesmo acontece com classes de modelo, mesmo se, neste caso, é um pouco menos intuitiva. Quando você declarar (e não definir) uma função amigo dentro do corpo de classe de modelo, você está declarando uma função livre com que os argumentos exatos. Note-se que você está declarando uma função, não uma função de modelo:

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

Essas funções livres são declarados mas não definido. A parte complicada é que essas funções livres não são um modelo, mas as funções livres regulares sendo declarado. Quando você adiciona a função de modelo na mistura você tem:

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

Quando o compilador atinge a função principal que instancia o template_test modelo com tipo int e que declara o void f( template_test<int> const & ) função livre que não é templated. Quando encontra o f( t1 ) chamada existem dois símbolos f que correspondem: a f( template_test<int> const & ) sem gabarito declarado (e não definido) quando template_test foi instanciado ea versão templated que é declarada e definida pelo 1. A versão não-templated tem precedência eo compilador corresponde.

Quando as tentativas de vinculador para resolver a versão não-templated de f ele não pode encontrar o símbolo e, assim, falhar.

O que podemos fazer? Há duas soluções diferentes. No primeiro caso, nós fazemos o compilador fornecer funções não templated para cada tipo instantiating. No segundo caso, nós declaramos a versão templated como um amigo. Eles são sutilmente diferentes, mas na maioria dos casos equivalente.

Ter o compilador gerar as funções não-modeladas para nós:

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

Isto tem o efeito de criar o maior número de funções não-templated gratuitos, conforme necessário. Quando o compilador encontra a declaração amigo dentro do test modelo que não só encontra a declaração, mas também a implementação e adiciona tanto ao âmbito de fechamento.

Fazendo a versão templated um amigo

Para fazer o modelo de um amigo, devemos tê-lo já declarados e dizer ao compilador que o amigo que queremos é realmente um modelo e não uma função não-templated livre:

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 & ) {}

Neste caso, antes de declarar f como um modelo que deve declarar frente do modelo. Para declarar o modelo f devemos primeiro declarar frente o modelo test. A declaração amigo é modificado para incluir os colchetes que identificam que o elemento que estamos a fazer um amigo é realmente um modelo e não uma função livre.

Voltar para o problema

Voltando ao seu exemplo particular, a solução mais simples é ter o compilador gerar as funções para você por inlining a declaração da função amigo:

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

Com esse código você está forçando o compilador para gerar um operator<< não-templated para cada tipo instanciado, e que os delegados função gerada no método dump do modelo.

Outras dicas

Você não precisa a declaração do operador de modelos e você tem que declarar o operador "amigo" para sua classe para ter acesso concedido a outras classes, neste caso std :: cout

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

leitura recomendado: http://www.parashift.com/c++- faq-lite / friends.html

Quando sobrecarregar o operador << você quiser usar uma referência const:

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

Certifique-se as definições de modelo completo (e não apenas protótipos) estão no arquivo de inclusão (ou seja, .h, .hpp) como modelos e compilação separada não funcionam em conjunto.

Eu não sei o vinculador @Dribeas está usando, mas isso pode certamente causar o vinculador GNU para dar um erro de referência indefinida.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top