Как перегрузить ostream для структуры члена шаблонного класса - PullRequest
0 голосов
/ 12 ноября 2018

Я реализую словарь в C ++, используя двоичное дерево, каждый узел в моем дереве имеет ключ (int), элемент (string) и левый и правый дочерний элемент.

Во время этой реализации я перегрузил оператор ostream для моего BinaryTree класса для распечатки содержимого дерева.

Кроме того, я перегрузил ostream для работы с Node указателями, которые затем распечатали бы клавишу и элемент этого узла.

Это сработало нормально. Однако когда я попытался сделать дерево шаблоном для работы с любым типом перегрузки моего ключа или элемента, эти операторы стали более трудными.

Я выделил проблему, чтобы упростить работу, кроме того, я попытался поиграться как с узлом , так и с указателем узла , чтобы посмотреть, смогу ли я получить один к работать без другого.

Вот класс, который я создал для проверки проблемы, этот класс не шаблонизирован и работает нормально.

test.h

class myClass
{
public:
    using Key = int;
    myClass(Key);
    friend std::ostream & operator<<(std::ostream &, const myClass &);
private:
    struct Thing {
        Key data;
        Thing();
        Thing(Key);
    };
    Thing* B;
    Thing A;

    void disp(std::ostream &) const;
    friend std::ostream & operator<<(std::ostream &, myClass::Thing);
    friend std::ostream & operator<<(std::ostream &, myClass::Thing *);
};

test.cpp

myClass::Thing::Thing(Key Num) { data = Num; }

myClass::myClass(Key Num)
{
    A = Thing(Num); B = &A;
}

void myClass::disp(std::ostream & os) const
{
    os << A << std::endl;   os << B << std::endl;
}

std::ostream & operator<<(std::ostream & os, const myClass & c)
{
    c.disp(os); return os;
}

std::ostream & operator<<(std::ostream & os, myClass::Thing th)
{
    os << th.data;  return os;
}

std::ostream & operator<<(std::ostream & os, myClass::Thing *th)
{
    os << th->data;     return os;
}

С помощью этого класса я могу легко создать экземпляр моего класса и std::cout, который выдает результат, как ожидалось. Затем превращаем этот класс в шаблон:

template <class T> class myTemplate
{
public:
    using Key = T;
    myTemplate(Key);
    template<class A>
    friend std::ostream & operator<<(std::ostream &, const myTemplate<A> &);
private:
    struct Thing;
    Thing A;
    Thing* B;
    void disp(std::ostream &) const;
    template <class A>  friend std::ostream & operator<<(std::ostream &, typename myTemplate<A>::Thing);
    template <class A>  friend std::ostream & operator<<(std::ostream &, typename myTemplate<A>::Thing *);
};

template <class T> struct myTemplate<T>::Thing
{
    T data;
    Thing() = default;
    Thing(Key);
};
//Create new thing A with B a pointer to A
template <class T> myTemplate<T>::myTemplate(Key Num)
{
    A = Thing(Num);
    B = &A;
}
//Displays Node A & B
template <class T> void myTemplate<T>::disp(std::ostream & os) const
{
    os << A << std::endl;   os << B << std::endl;
}
template <class T> myTemplate<T>::Thing::Thing(Key Num)
{
    data = Num;
}
//Overloading << will call disp function, in turn print A & B to stream
template<class T> std::ostream & operator<<(std::ostream & os, const myTemplate<T> & c)
{
    c.disp(os);     return os;
}
//Output a to stream
template <class A> std::ostream & operator<<(std::ostream & os, typename myTemplate<A>::Thing th)
{
    os << th.data;  return os;
}
//Output a to stream
template <class A> std::ostream & operator<<(std::ostream & os, typename myTemplate<A>::Thing *th)
{
    os << th->data;     return os;
}

Однако с myTemplate, когда я пытался в main():

myTemplate Template(5);    
cout << Template;

Код не скомпилируется, так как я получаю сообщение об ошибке:

Error   C2679   binary '<<': no operator found which takes a right-hand operand of type 'const myTemplate<std::string>::Thing' (or there is no acceptable conversion)

Дополнительно комментируем строку:

os << A << std::endl;

Таким образом, только B выводится в поток, код скомпилируется. Однако данные B не выводятся, только адрес памяти B.

Я заметил, что при использовании точек останова при попытке вывести B код даже не использует определенную мной функцию перегрузки. Это не относится к классу без шаблонов, поскольку определенные мной перегрузки используются как для A, так и для B.

Итак, как правильно перегрузить оператор ostream для работы с элементом struct?

Извиняется за длинный вопрос, чувствовал, что я должен включить то, что я сам определил.

1 Ответ

0 голосов
/ 12 ноября 2018

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

#include <iostream>

template <class T> class myTemplate
{
public:
    using Key = T;
private:
    struct Thing
    {
        T data;
        Thing() = default;
        Thing(Key Num) : data(Num) {}
    };
    Thing A;
    Thing* B = nullptr;
public:
    myTemplate(Key Num) : A(Thing(Num)), B(&A) {}    

    friend std::ostream & operator<<(std::ostream& out, const myTemplate &obj)
    {
        return out << obj.A << std::endl << obj.B << std::endl;
    }

    friend std::ostream & operator<<(std::ostream& out, typename myTemplate::Thing thing)
    {
        return out << thing.data;
    }

    friend std::ostream & operator<<(std::ostream& out, typename myTemplate::Thing *thing)
    {
        return out << thing->data;
    }
};

int main()
{
    myTemplate<int> obj(10);
    std::cout << obj;
    return 0;
}

Обновление : Если конечная цель обеспечения перегрузки two operator<< на структуру Thing - это просто удобный вызов в operator<< myTemplate класс, тогда вам это не нужно, просто напечатайте data непосредственно в operator<< класса myTemplate. Это снова уменьшит значительное количество кодов (, если это так! ).

Тем не менее, теперь вы можете предоставить специализацию для функции, не являющейся членом (другом) (т.е. operator<<) для класса myTemplate, следующим образом:

template <class T> class myTemplate; // forward declaration
template <class T> std::ostream& operator<<(std::ostream& out, const myTemplate<T> &obj);

template <class T> class myTemplate
{
private:
    using Key = T;
private:
    template <class Type = T> struct Thing
    {
        Type data;
        Thing() = default;
        Thing(const Key Num) : data(Num) {}
    };
private:
    Thing<> A;
    Thing<> *B = nullptr;
public:
    myTemplate(const Key Num) : A(Thing<>(Num)), B(&A) {}

    friend std::ostream & operator<<<>(std::ostream& out, const myTemplate &obj);
};

template <class T>  std::ostream & operator<<(std::ostream& out, const myTemplate<T> &obj)
{
    return out << obj.A.data << std::endl << obj.B->data << std::endl;
}
...