为模板类中的>>运算符和<<运算符实现重载

Implementing overloads for >> and << operators in a template class

本文关键字:lt 运算符 gt 实现 重载      更新时间:2023-10-16

我正在尝试编写函数定义,以便在类定义之外重载运算符">>"和"<<",尽管在同一个文件中,如您所见。我收到以下错误:

1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class MyClass<int> &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$MyClass@H@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class MyClass<int>)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@V?$MyClass@H@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class MyClass<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$MyClass@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class MyClass<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@V?$MyClass@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z) referenced in function _main

我和这个一起在河上上下下。奇怪的是,如果我在类定义中编写他们的定义,它会完美地工作。

#include <iostream>
#include <string>
using namespace std;
template <class MYTYPE>
class MyClass {
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {};
    friend istream& operator>>(istream&, MyClass<MYTYPE>&);
    friend ostream& operator<<(ostream&, MyClass<MYTYPE>);
};
template <class MYTYPE>
MyClass<MYTYPE>::MyClass(int x, string y) {
    dim = x;
    name = y;
    myVector = new MYTYPE[dim];
}

template <class MYTYPE>
istream& operator>>(istream& X, MyClass<MYTYPE>& a){
    cout<<"Reading vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++){
        cout<<a.name<<'['<<indice<<"]= ";
        X >> a.myVector[indice];
    }
    return X;
}
template <class MYTYPE>
ostream& operator<<(ostream& X, MyClass<MYTYPE> a){
    X<<"Vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++)
        X<<a.myVector[indice]<<' ';
        X<<endl;
    return X;
}

int main() {
    MyClass<int> object(4, "Ints vector");
    MyClass<string> object2(5, "String vector");
    cin >> object;
    cin >> object2;
    cout << object;
    cout << object2;
    system("pause");
    return 0;
}

在您的代码中,MyClass 中的友元运算符声明应如下所示:

template<typename T> friend istream& operator>>(istream&, MyClass<T>&);
template<typename T> friend ostream& operator<<(ostream&, MyClass<T>);

也就是说,它们应具有自己的模板参数。

基于问题中提供的定义,MyClass的有效定义如下:

template <class MYTYPE>
class MyClass { 
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {}
    template<typename T>
    friend istream& operator>>(istream&, MyClass<T>&);
    template<typename T>
    friend ostream& operator<<(ostream&, MyClass<T>);
};

打开编译器的警告级别会有所帮助。通过将-Wallg++一起使用,我在链接器错误之前收到以下警告。

socc.cc:13:58: warning: friend declaration ‘std::istream& operator>>(std::istream&, MyClass<MYTYPE>&                    )’ declares a non-template function [-Wnon-template-friend]
     friend istream& operator>>(istream&, MyClass<MYTYPE>&);
                                                          ^
socc.cc:13:58: note: (if this is not what you intended, make sure the function template has already                     been declared and add <> after the function name here)
socc.cc:14:57: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, MyClass<MYTYPE>                     ’ declares a non-template function [-Wnon-template-friend]
     friend ostream& operator<<(ostream&, MyClass<MYTYPE>);

您需要对operator>>operator<<函数使用函数模板。您可以在类的定义之前声明它们:

// Forward the class template.
template <class MYTYPE> class MyClass;
// Declare the function templates.
template <class MYTYPE> 
std::istream& operator>>(std::istream&, MyClass<MYTYPE>&);
template <class MYTYPE> 
std::ostream& operator<<(st::ostream&, MyClass<MYTYPE>);

然后,必须将 friend 声明与相应的模板参数一起使用。

// This makes sure that operator>><int> is not a friend of MyClass<double>
// Only operator>><double> is a friend of MyClass<double>
friend std::istream& operator>><MYTYPE>(std::istream&, MyClass<MYTYPE>&);
friend std::ostream& operator<<<MYTYPE>(std::ostream&, MyClass<MYTYPE>);

这是为我构建的代码的更新版本。我还没有尝试运行它。

#include <iostream>
#include <string>
using namespace std;
template <class MYTYPE> class MyClass;
template <class MYTYPE> 
std::istream& operator>>(std::istream&, MyClass<MYTYPE>&);
template <class MYTYPE> 
std::ostream& operator<<(std::ostream&, MyClass<MYTYPE>);
template <class MYTYPE>
class MyClass {
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {};
    friend std::istream& operator>><MYTYPE>(std::istream&, MyClass<MYTYPE>&);
    friend std::ostream& operator<<<MYTYPE>(std::ostream&, MyClass<MYTYPE>);
};
template <class MYTYPE>
MyClass<MYTYPE>::MyClass(int x, string y) {
    dim = x;
    name = y;
    myVector = new MYTYPE[dim];
}

template <class MYTYPE>
std::istream& operator>>(std::istream& X, MyClass<MYTYPE>& a){
    cout<<"Reading vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++){
        cout<<a.name<<'['<<indice<<"]= ";
        X >> a.myVector[indice];
    }
    return X;
}
template <class MYTYPE>
std::ostream& operator<<(std::ostream& X, MyClass<MYTYPE> a){
    X<<"Vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++)
        X<<a.myVector[indice]<<' ';
        X<<endl;
    return X;
}

int main() {
    MyClass<int> object(4, "Ints vector");
    MyClass<string> object2(5, "String vector");
    cin >> object;
    cin >> object2;
    cout << object;
    cout << object2;
    system("pause");
    return 0;
}

这有点复杂。实际上,类模板有两种方式可以拥有函数友元。

例如,以这个为例:

template <typename T>
void fun();
template <typename T>
class A
{
};

那你想要什么?

  1. 或者你想让func<X>()成为A<Y>的朋友,Y
  2. 你想让fun<X>()成为A<X>的朋友而不是A<Y>的朋友吗?

也就是说,函数是否应该能够访问任何专用化的私有成员,还是只能访问相同类型的专用化。

我的猜测是您需要选项 2。诀窍是你必须成为函数专用化的朋友,所以模板函数必须已经存在:你首先声明了模板函数,然后让它成为朋友。像这样:

//first declare both function and class
template <typename T>
class A;
template <typename T>
void fun();
//Then define the class
template <typename T>
class A
{
    //A<T> is friend to fun<T>, the specializations must match.
    friend void fun<T>();
};
//And define the function
template <typename T>
void fun()
{
}

如果您想要的是选项 1,则不需要高级声明。只:

template <typename T>
class A
{
    //A<T> is friend to fun<X>, T and X may be different
    template <class X>
    friend void fun();
};

也就是说,由于<<>>,您的代码编写起来有点棘手:

template <class MYTYPE>
class MyClass;
template <class MYTYPE>
istream& operator>>(istream& X, MyClass<MYTYPE>& a);
template <class MYTYPE>
ostream& operator<<(ostream&, MyClass<MYTYPE>);
template <class MYTYPE>
class MyClass {
    // ....
    friend istream& operator>> <MYTYPE>(istream&, MyClass<MYTYPE>&);
    friend ostream& operator<< <MYTYPE>(ostream&, MyClass<MYTYPE>);
};