2014-11-25 1 views
1

Этот вопрос имеет подробный ответ здесь: Overloading operator<<: cannot bind lvalue to ‘std::basic_ostream<char>&&’C++: оператор << перегрузки во вложенных классов

Я пытаюсь перегружать вложенный подкласс, и в течение часа, пытаясь перегрузить operator<<. Выяснилось немного здесь, но все равно не могло его решить. Любая помощь? :)

Всякий раз, когда я пытаюсь компилировать его с помощью g++ -std=c++11 -lm -ggdb -g -O0 -Wall p_7_1.cpp -o p_7_1, он дает мне ошибку:

Undefined symbols for architecture x86_64: 
    "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, LinkedBinaryTree<int>::Position const&)", referenced from: 
     std::__1::basic_ostream<char, std::__1::char_traits<char> >& operator<<<int>(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, LinkedBinaryTree<int> const&) in p_7_1-9fc2c2.o 
ld: symbol(s) not found for architecture x86_64 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

p_7_1.cpp:

#include "tree.hpp" 

typedef LinkedBinaryTree<int> Tree; 


#include <iostream> 

using namespace std; 


int main() { 
    // Test if tree works: 
    Tree lbt; 
    cout << lbt.empty() << endl; 
    lbt.addRoot(); 
    lbt.addRoot(); 
    cout << lbt.empty() << endl; 
    cout << lbt.size() << endl; 
    lbt.expandExternal(lbt.root()); 
    cout << lbt.empty() << endl; 
    cout << lbt.size() << endl; 

    *(lbt.root()) = 12; 
    cout << lbt; 
} 

tree.hpp:

#ifndef LINKED_BINARY_TREE_HPP 
#define LINKED_BINARY_TREE_HPP 

#include <list> 

template <typename T> class LinkedBinaryTree; 
template <typename T> std::ostream& operator<<(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
template <typename T> std::ostream& operator<<(std::ostream& os, const typename LinkedBinaryTree<T>::Position& p); 

template <typename T> 
class LinkedBinaryTree { 
protected: 
    struct Node {   // a node of the tree 
    T elt;  // element value 
    Node* par;  // parent 
    Node* left;  // left child 
    Node* right;  // right child 
    Node() : elt(), par(NULL), left(NULL), right(NULL) { } // constructor 
    }; 

public: 
    class Position {  // position in the tree 
    private:   // 
    Node* v;   // pointer to the node 
    public: 
    Position(Node* _v = NULL) : v(_v) { } // constructor 
    T& operator*()   // get element 
    { return v->elt; }   // 
    Position left() const   // get left child 
    { return Position(v->left); }  // 
    Position right() const  // get right child 
    { return Position(v->right); } // 
    Position parent() const  // get parent 
    { return Position(v->par); }  // 
    bool isRoot() const   // root of the tree? 
    { return v->par == NULL; }  // 
    bool isExternal() const  // an external node? 
    { return v->left == NULL && v->right == NULL; } // 
    friend class LinkedBinaryTree; // give tree access 
    public: 
    friend std::ostream& operator<<(std::ostream& os, const typename LinkedBinaryTree<T>::Position& p); 
    friend std::ostream& operator<< <T>(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
    }; 
    typedef std::list<Position> PositionList; // list of positions 
public:      // 
    LinkedBinaryTree();    // constructor 
    int size() const;    // number of nodes 
    bool empty() const;    // is tree empty? 
    Position root() const;   // get the root 
    PositionList positions() const;  // list of nodes 
    void addRoot();    // add root to empty tree 
    void expandExternal(const Position& p); // expand external node 
    Position removeAboveExternal(const Position& p); // remove p and parent 
       // housekeeping functions omitted... 
protected:   // local utilities 
    void preorder(Node* v, PositionList& pl) const; // preorder utility 
public: 
    friend std::ostream& operator<< <T>(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
private:      // 
    Node* _root;   // pointer to the root 
    int n;   // number of nodes 
};    // 

template <typename T> 
LinkedBinaryTree<T>::LinkedBinaryTree() // constructor 
    : _root(NULL), n(0) { } 

template <typename T> 
int LinkedBinaryTree<T>::size() const // number of nodes 
{ return n; } 

template <typename T> 
bool LinkedBinaryTree<T>::empty() const // is tree empty? 
{ return size() == 0; } 

template <typename T> 
typename LinkedBinaryTree<T>::Position LinkedBinaryTree<T>::root() const // get the root 
{ return Position(_root); } 

template <typename T> 
typename LinkedBinaryTree<T>::PositionList LinkedBinaryTree<T>::positions() const { 
    PositionList pl; 
    preorder(_root, pl);  // preorder traversal 
    return PositionList(pl); // return resulting list 
} 

template <typename T> 
void LinkedBinaryTree<T>::addRoot() // add root to empty tree 
{ _root = new Node; n = 1; } 

template <typename T> 
void LinkedBinaryTree<T>::expandExternal(const Position& p) { 
    Node* v = p.v;  // p's node 
    v->left = new Node;  // add a new left child 
    v->left->par = v;  // v is its parent 
    v->right = new Node;  // and a new right child 
    v->right->par = v;  // v is its parent 
    n += 2;   // two more nodes 
} 

template <typename T> 
typename LinkedBinaryTree<T>::Position // remove p and parent 
LinkedBinaryTree<T>::removeAboveExternal(const Position& p) { 
    Node* w = p.v; Node* v = w->par; // get p's node and parent 
    Node* sib = (w == v->left ? v->right : v->left); 
    if (v == _root) {  // child of root? 
    _root = sib;  // ...make sibling root 
    sib->par = NULL; 
    } 
    else { 
    Node* gpar = v->par;   // w's grandparent 
    if (v == gpar->left) gpar->left = sib; // replace parent by sib 
    else gpar->right = sib; 
    sib->par = gpar; 
    } 
    delete w; delete v;  // delete removed nodes 
    n -= 2;   // two fewer nodes 
    return Position(sib); 
} 

// preorder traversal 
template <typename T> 
void LinkedBinaryTree<T>::preorder(Node* v, PositionList& pl) const { 
    pl.push_back(Position(v)); // add this node 
    if (v->left != NULL)  // traverse left subtree 
    preorder(v->left, pl); 
    if (v->right != NULL)  // traverse right subtree 
    preorder(v->right, pl); 
} 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const LinkedBinaryTree<T>& lbt){ 
    os << lbt.root(); 
    // os << *(lbt.root()); 
    return os; 
} 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const typename LinkedBinaryTree<T>::Position& p) { 
    os << *p;   // Other func stuff will be here later 
    return os; 
} 

#endif 

UPDATE : Существует explanation by david-rodríguez-dribeas о перегрузке оператора в вложенные классы. Он предлагает лучше объявить operator<< inline.

+0

я могу заставить его работать, если я объявляю оператора друга в классе: '' 'класса A {класса B { friend std :: ostream & operator << (...) {...}}; }; '' '. Я все еще не могу заставить его работать, если я возьму определение из класса – RafazZ

+0

Скомпилируйте с помощью -Wall и на самом деле прочитайте ваши предупреждения –

+0

возможный дубликат [Перегрузка оператора шаблона шаблона C++] (http://stackoverflow.com/questions/3989678/c -template-friend-operator-overloading) –

ответ

1

После прочтения всего дня, я чувствую, что нашел что-то близкое к решению. Я посоветовал от here и поместил объявление в строку. В дополнение к этому мне пришлось обновить свой GCC до 4.9 (раньше использовал 4.2.1) и WHOOOAAAhhh - они изменили способ поведения (немного). В любом случае, похоже, что лучшим решением для вложенных классов является встроенное определение. Фиксированный код приведен ниже.

p_7_1.cpp:

#include "tree.hpp" 

typedef LinkedBinaryTree<int> Tree; 

#include <iostream> 

using namespace std; 

int main() { 
    // Test if tree works: 
    Tree lbt; 
    cout << lbt.empty() << endl; 
    lbt.addRoot(); 
    lbt.addRoot(); 
    cout << lbt.empty() << endl; 
    cout << lbt.size() << endl; 
    lbt.expandExternal(lbt.root()); 
    cout << lbt.empty() << endl; 
    cout << lbt.size() << endl; 

    // rotateLeft(lbt.root().right()); 
    *(lbt.root()) = 12; 
    *(lbt.root().left()) = 11; 
    *(lbt.root().right()) = 13; 
    cout << lbt; 
} 

tree.hpp:

#ifndef LINKED_BINARY_TREE_HPP 
#define LINKED_BINARY_TREE_HPP 

#include <cstdlib> 
#include <iostream> 
#include <list> 

template <typename T> class LinkedBinaryTree; 
template <typename T> std::ostream& operator<<(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
template <typename T> std::ostream& operator<<(std::ostream& os, const typename LinkedBinaryTree<T>::Position& p); 

template <typename T> 
class LinkedBinaryTree { 
protected: 
    struct Node {   // a node of the tree 
    T elt;  // element value 
    Node* par;  // parent 
    Node* left;  // left child 
    Node* right;  // right child 
    Node() : elt(), par(NULL), left(NULL), right(NULL) { } // constructor 
    }; 

public: 
    class Position {  // position in the tree 
    private:   // 
    Node* v;   // pointer to the node 
    public: 
    Position(Node* _v = NULL) : v(_v) { } // constructor 
    T& operator*()   // get element 
    { return v->elt; }   // 
    Position left() const   // get left child 
    { return Position(v->left); }  // 
    Position right() const  // get right child 
    { return Position(v->right); } // 
    Position parent() const  // get parent 
    { return Position(v->par); }  // 
    bool isRoot() const   // root of the tree? 
    { return v->par == NULL; }  // 
    bool isExternal() const  // an external node? 
    { return v->left == NULL && v->right == NULL; } // 
    friend class LinkedBinaryTree; // give tree access 
    public: 
    //friend std::ostream& operator<< <T> (std::ostream& os, const LinkedBinaryTree<T>::Position& p); 
    friend inline std::ostream& operator<<(std::ostream& os, const Position& p) { 
     os << '['; 
     if (!p.isExternal()){ 
    os << p.left(); 
     } 
     os << ' '; 
     os << *(Position(p)); 
     os << ' '; 
     if (!p.isExternal()) { 
    os << p.right(); 
     } 
     os << ']'; 

     return os; 
    } 

    friend std::ostream& operator<< <T>(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
    }; 
    typedef std::list<Position> PositionList; // list of positions 
public:      // 
    LinkedBinaryTree();    // constructor 
    int size() const;    // number of nodes 
    bool empty() const;    // is tree empty? 
    Position root() const;   // get the root 
    PositionList positions() const;  // list of nodes 
    void addRoot();    // add root to empty tree 
    void expandExternal(const Position& p); // expand external node 
    Position removeAboveExternal(const Position& p); // remove p and parent 
       // housekeeping functions omitted... 
protected:   // local utilities 
    void preorder(Node* v, PositionList& pl) const; // preorder utility 
public: 
    friend std::ostream& operator<< <T>(std::ostream& os, const LinkedBinaryTree<T>& lbt); 
private:      // 
    Node* _root;   // pointer to the root 
    int n;   // number of nodes 
};    // 

template <typename T> 
LinkedBinaryTree<T>::LinkedBinaryTree() // constructor 
    : _root(NULL), n(0) { } 

template <typename T> 
int LinkedBinaryTree<T>::size() const // number of nodes 
{ return n; } 

template <typename T> 
bool LinkedBinaryTree<T>::empty() const // is tree empty? 
{ return size() == 0; } 

template <typename T> 
typename LinkedBinaryTree<T>::Position LinkedBinaryTree<T>::root() const // get the root 
{ return Position(_root); } 

template <typename T> 
typename LinkedBinaryTree<T>::PositionList LinkedBinaryTree<T>::positions() const { 
    PositionList pl; 
    preorder(_root, pl);  // preorder traversal 
    return PositionList(pl); // return resulting list 
} 

template <typename T> 
void LinkedBinaryTree<T>::addRoot() // add root to empty tree 
{ _root = new Node; n = 1; } 

template <typename T> 
void LinkedBinaryTree<T>::expandExternal(const Position& p) { 
    Node* v = p.v;  // p's node 
    v->left = new Node;  // add a new left child 
    v->left->par = v;  // v is its parent 
    v->right = new Node;  // and a new right child 
    v->right->par = v;  // v is its parent 
    n += 2;   // two more nodes 
} 

template <typename T> 
typename LinkedBinaryTree<T>::Position // remove p and parent 
LinkedBinaryTree<T>::removeAboveExternal(const Position& p) { 
    Node* w = p.v; Node* v = w->par; // get p's node and parent 
    Node* sib = (w == v->left ? v->right : v->left); 
    if (v == _root) {  // child of root? 
    _root = sib;  // ...make sibling root 
    sib->par = NULL; 
    } 
    else { 
    Node* gpar = v->par;   // w's grandparent 
    if (v == gpar->left) gpar->left = sib; // replace parent by sib 
    else gpar->right = sib; 
    sib->par = gpar; 
    } 
    delete w; delete v;  // delete removed nodes 
    n -= 2;   // two fewer nodes 
    return Position(sib); 
} 

// preorder traversal 
template <typename T> 
void LinkedBinaryTree<T>::preorder(Node* v, PositionList& pl) const { 
    pl.push_back(Position(v)); // add this node 
    if (v->left != NULL)  // traverse left subtree 
    preorder(v->left, pl); 
    if (v->right != NULL)  // traverse right subtree 
    preorder(v->right, pl); 
} 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const LinkedBinaryTree<T>& lbt){ 
    os << lbt.root(); 
    os << std::endl; 
    // os << *(lbt.root()); 
    return os; 
} 

/* 
template <typename T> 
std::ostream& operator<<(std::ostream& os, const typename LinkedBinaryTree<T>::Position& p) { 
    os << '['; 
    if (!p.isExternal()){ 
    os << p.left(); 
    } 
    os << ' '; 
    os << *(Position(p)); 
    os << ' '; 
    if (!p.isExternal()) { 
    os << p.right(); 
    } 
    os << ']'; 

    return os; 
} 
*/ 
#endif 
+0

Обратите внимание, что это объявление 'template std :: ostream & operator << (std :: ostream & os, const typename LinkedBinaryTree :: Position & p);' никогда не используется. Более того, это бесполезно из-за * не выводимого контекста *. Вы можете (и, вероятно, должны) удалить его. –

+0

О, я думал, что должен объявить его, потому что я использую его в 'std :: ostream & operator << (std :: ostream & os, const LinkedBinaryTree & lbt) {' - Есть ли более эффективный способ? – RafazZ

+0

Вы не используете это объявление, оно никогда не подбирается при разрешении перегрузки. Попробуйте удалить встроенный 'operator <<' и посмотреть, какие ошибки компилятора (а не компоновщика!). –