在下面的代码中,我举例说明了运算符重载:
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A() {};
A( T &obj) {value = obj;};
~A() {};
T value;
template <typename E>
A<T>& operator = (const A<E> &obj)
{
cout<<"equal operator"<<endl;
if(this == &obj)
return *this;
value = obj.value;
return *this;
}
};
int main()
{
int temp;
temp = 3;
A<int> myobjects(temp);
cout<<myobjects.value<<endl;
temp = 7;
A<int> yourobjects(temp);
yourobjects = myobjects;
cout<<yourobjects.value<<endl;
return 0;
}
但是,当我调试这个程序时,我发现主程序没有调用相等的运算符重载函数。但是,如果我按以下方式更改等于运算符:
A<T>& operator = (const A<T> &obj)
{
cout<<"equal operator"<<endl;
if(this == &obj)
return *this;
value = obj.value;
return *this;
}
它会起作用。你有什么想法为什么初始功能不起作用?
答案 0 :(得分:7)
您的模板版本的赋值运算符不会抑制为您的类生成编译器提供的非模板复制赋值运算符。编译器将隐含地声明和定义具有以下签名的复制赋值运算符
A<T>& operator =(const A<T>&);
在复制赋值的重载解析过程中,编译器提供的版本获胜(因为它更专业)。
您的模板版本的赋值运算符仅用于转换赋值。例如。如果在某些时候您想要将A<int>
对象分配给A<double>
对象,则将使用您的模板版本的赋值运算符。但是当您将A<int>
分配给A<int>
时,您的运算符将被忽略,因为编译器声明的版本是更好的匹配。
使用
声明自己的副本分配版本时A<T>& operator =(const A<T>&);
签名,它会抑制编译器生成的签名。你的版本被使用了。
这意味着如果您希望将自己的复制赋值运算符以及作为模板转换赋值运算符,则需要在类中明确实现 both 。
P.S。作为'@Cheers和hth。 - Alf'正确地指出,你的模板版本的赋值运算符在一般情况下甚至都不是有效的。指针this
和&obj
通常具有不同的不相关类型。您不能比较不同无关类型的指针。
答案 1 :(得分:4)
如果您没有定义自己的复制赋值运算符,编译器会自动声明并定义带有签名T& operator=(T const&)
的复制赋值运算符。
您的赋值运算符模板不是复制赋值运算符,因为它是一个模板(模板永远不是复制赋值运算符)。
在您的示例中,myobjects
和yourobjects
都是A<int>
类型。编译器可以选择两个operator=
重载:
A<int>& operator=(A<int> const&)
,由编译器隐式提供,或A<int>& operator=(A<int> const&)
,您模板的专业化。您会注意到这两者具有完全相同的签名。重载决策的一个规则是所有其他条件相同,非模板优于模板。隐式声明的复制赋值运算符不是模板,并且您的重载是模板,因此选择了隐式声明的operator=
。
要禁止隐式声明的复制赋值运算符,您需要声明并定义自己的:
A& operator=(A const&) { /* etc. */ }