Реализация перегрузок для >> и << операторов в классе шаблона

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

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> &)" ([email protected][email protected][email protected]@[email protected]@@[email protected]@[email protected][email protected]@@@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>)" ([email protected][email protected][email protected]@[email protected]@@[email protected]@[email protected][email protected]@@@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> > > &)" ([email protected][email protected][email protected]@[email protected]@@[email protected]@[email protected][email protected][email protected][email protected]@std@@[email protected]@[email protected]@[email protected]@@@@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> > >)" ([email protected][email protected][email protected]@[email protected]@@[email protected]@[email protected][email protected][email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@@@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;
}

Ответ 1

Помогает повысить уровень предупреждения компилятора. Используя -Wall с g++, я получаю следующие предупреждения перед ошибкой компоновщика.

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

Ответ 2

В вашем коде объявления операторов друзей в 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>);
};

Ответ 3

Это немного сложно. На самом деле есть два способа, которыми шаблон шаблона может иметь друга функции.

Например, возьмите это:

template <typename T>
void fun();

template <typename T>
class A
{
};

Тогда что вам нужно?

  • Вы хотите, чтобы fun<X>() был другом A<X>, но не A<Y>?
  • Или вы хотите, чтобы func<X>() был другом A<Y> для любого другого 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>);
};