Перегрузка << оператора на шаблонных классах - PullRequest
4 голосов
/ 11 января 2012

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

#include <iostream>
using namespace std;
template<class NType> class Node;

template<class NType>
class Node {
private:
        void deletePointer(NType* p);
public:
        NType data;
        Node *prev, *next;

        template<typename T>
        struct is_pointer { static const bool value = false; };

        template<typename T>
        struct is_pointer<T*> { static const bool value = true; };

        Node();
        Node(NType data);
        ~Node();
};  

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}

template<class NType> inline std::ostream & operator << (std::ostream& out, const Node<NType> &node){
        out << node.data;
        return out;
}

template<class NType> inline Node<NType>::Node()
            :data(NULL), prev(NULL), next(NULL)
{
}

template<class NType> inline Node<NType>::Node(NType data)
            :data(data), prev(NULL), next(NULL)
{
}

template<class NType> inline Node<NType>::~Node(){
        if(is_pointer<NType>::value){
                deletePointer(&data);
        } else {
                return;
        }
}

template<class NType> inline void Node<NType>::deletePointer(NType* p){
    delete p;
}

Выводит ячейки памяти, а не данные внутри узлов. Это происходит с примитивными типами, такими как int и т. Д., Как если бы он не знал, какие данные были в контейнере NType.

Node 1: 0x741010
Node 2: 0x741030
Node 3: 0x741070
Node 4: 0x741090

Я пытался использовать typename вместо class, но до сих пор не играли в кости ... Есть ли способ динамически выяснить, какой тип шаблона используется и приводится или что-то еще до вставки? Я знаю, что могу сделать тонну избыточного кода для всех примитивов, но это кажется расточительным и ненужным.

Если это поможет, я собираюсь на Arch Linux x64 с GCC v4.6.2 20111223

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

Edit: Контрольный пример: http://ideone.com/a99u5 Также обновлен источник выше.

Edit: Добавил оставшуюся часть моего кода, чтобы помочь Аарону в его понимании кода.

Ответы [ 5 ]

7 голосов
/ 11 января 2012

Ваш код объявляет operator<< как функцию-член, поэтому он фактически принимает указатель this в качестве первого аргумента и ostream в качестве второго.Вместо этого она должна быть бесплатной функцией:

template<class NType> class Node {
public:
    NType data;
    Node *prev, *next;
};
//Note how this is declared outside of the class body, so it is a free function instead of a memberfunction
template<class NType> inline std::ostream& operator<<(std::ostream& out, const Node<NType>& val){
    out << val.data;
    return out;
}

, однако, если вашему operator<< нужен доступ к личным данным, вам нужно объявить его как функцию друга:

template<class NType> class Node {
public:
    NType data;
    Node *prev, *next;
    friend std::ostream& operator<<(std::ostream& out, const Node& val){
        out << val.data;
        return out;
    }
};

Сейчасдля вашего вывода: если ваш operator<< был вызван, компилятор будет знать тип NType и будет делать правильные вещи при потоковой передаче члена data.Однако, поскольку ваш operator<< не должен был работать (как написано), и, похоже, он выдал вам адреса памяти в качестве вывода, я бы предположил, что у вас есть что-то вроде следующего:

Node* n = new Node();
std::cout<<n;
//When it should be:
std::cout<<*n;

Теперь просто для любопытства:вы сами реализуете то, что выглядит как связанный список, вместо того, чтобы просто использовать std::list?

Edit: Теперь, когда мы можем увидеть тестовый пример, кажется, что предположения о том, как operator<< былзвонил было правильно.Выходные данные должны быть изменены на:

std::cout << "Node 1: " << *n1 << std::endl;
std::cout << "Node 2: " << *n2 << std::endl;

для фактического вызова operator<< для Node вместо общего для T*

3 голосов
/ 11 января 2012

Почему вы добавляете template<class NType> перед вашим уже настроенным методом класса?

Как правило, лучший способ перегрузки оператора << - это сделать его другом: </p>

template<typename NType>
friend std::ostream& operator<<(std::ostream& out, const Node<NType>& node)
{
    out << node.data;
    return out; 
}

Изменить: Чтобы ответить на комментарий ниже, я имею в виду:

Когда вы определяете шаблон класса в заголовке, вы не объявляете этот шаблон повторно для функций-членов класса:

template<typename T>
class Foo
{
    T some_data;

    void member_fn();
}

Это необходимо, когда вы объявляете функцию, не являющуюся другом, однако:

template<typename NType>
class Node 
{
public:

    NType data;
    Node<NType> *prev, *next;

    //Note the different template parameter!
    template<typename NType1>
    friend std::ostream& operator<<(std::ostream& out, const Node<NType1>& node);
};

Реализация этого затем становится вышеупомянутой template<typename NType> std::ostream& operator<<(std::ostream& out, const Node<NType>& node) реализацией.

1 голос
/ 11 января 2012
template <class NType>
class Node
{
  public:
    NType data;
    Node *prev, *next;
};

template <class NType>
inline std::ostream& operator<<(std::ostream& os, const Node<NType>& n)
{
    return out << n.data;
}
0 голосов
/ 11 января 2012

Другие отметили, что вам нужно использовать cout << *n1 вместо cout << n1, но в вашем коде есть еще одна важная ошибка.

Ваш деструктор включает вызов deletePointer(&data);, где data является членом класса. Это опасно Если эта строка кода когда-либо выполнялась, то программа, вероятно, вылетает. data - это всего лишь одна небольшая часть объекта, на которую указывает this, попытка удалить его - это все равно, что попытка удалить один элемент в массиве - целое число.

Node<int> *n1 = new Node<int>();
delete n1; // this makes sense. deleting the same thing that was new'ed

Node<int> *n1 = new Node<int>();
delete &(n1->data); // BUG

Вы, вероятно, должны сильно изменить свой дизайн и просто удалить специальный код для указателя. Вы ожидаете написать код вроде: Node<int*> *n2 = new Node<int*>( new int );? И если да, то как вы хотите, чтобы он вел себя?

Если NType на самом деле был типом указателя, как int*, тогда может иметь смысл сделать deletePointer(data), но это никогда не будет иметь смысла делать deletePointer(&data).

0 голосов
/ 11 января 2012

Вы печатаете адреса узлов, а не узлов:

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}

n1 и n2 являются указателями , а не объектами. Вы должны были написать:

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << *n1 << std::endl;
        std::cout << "Node 2: " << *n2 << std::endl;
}

или

int main() {
        Node<int> n1();
        Node<int> n2(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...