Реализация перегрузок для >> и << операторов в классе шаблона
Я пытаюсь написать определение функции для перегрузки операторов " → " и "< < вне определения класса, хотя в том же файле, как вы можете видеть. Я получаю следующие ошибки:
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>);
};