如何为模板化类的成员结构重载ostream

时间:2018-11-12 12:56:04

标签: c++ class c++11 templates operator-overloading

我正在使用二叉树在C ++中实现字典,树中的每个节点都有一个键({int),item(string)和左,右子级。

在此实现期间,我为BinaryTree类重载了 ostream运算符以打印出树的内容。

此外,我使 ostream 重载以使用Node指针,该指针随后将打印出 key item 该节点。

这很好。但是,当我试图将树作为模板以使其与任何类型的键或项目重载一起使用时,这些运算符就变得更加困难。

我隔离了问题以使其更容易处理,此外,我尝试同时使用 node 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时使用断点,该代码甚至没有使用我定义的重载函数。对于非模板类,情况并非如此,因为我定义的重载同时用于AB

那么重载ostream运算符为struct成员工作的正确方法是什么?

为这个冗长的问题表示歉意,觉得我应该把自己确定的内容包括在内。

1 个答案:

答案 0 :(得分:3)

由于模板实现本身将位于a single translate unit(头文件)中,因此,通过将它们拆散,您将不会获得更多收益。因此,将定义和非成员函数保留在类本身内部,这将为您提供清晰得多的代码,并提高模板类的可读性: See this

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

更新:如果要为结构operator<<提供两个Thing 重载的最终目的只是为了方便地调用{{ operator<<类的1}},那么您就不需要了,只需直接在myTemplate类的data中打印operator<<。这将再次减少大量代码(如果是这种情况,则)。

尽管如此,现在您可以为myTemplate类的非成员(朋友)功能(即operator<<)提供特殊化,如下所示:

myTemplate