运算符重载中的隐式转换问题

知识点:

1.隐式转换不会被用于非const的引用参数
2.二义性问题
3.为什么用友元函数

详情:

#include<iostream>
using namespace std;
class Complex
{
public:
    Complex(){ real = 0; imag = 0; }
    Complex(float r){ real = r; imag = 0; }//转换构造函数 
    Complex(float r, float i) :real(r), imag(i){}
    operator double() const { return real; } //转换操作符,必须为成员函数,形参列表为空,,一般不应该改变被转换的对象,通常定义为const
    friend Complex operator+(Complex&, Complex&);//没有二义性  
    //friend Complex operator+(const Complex&,const Complex &);//注释上边一句换为此句存在二义性,用显示转换消除二义性 20200302需注释转换操作符,执行单实参隐式转换,消除二义性20200302
    void display();
private:
    float real;
    float imag;
};
void Complex::display()
{
    cout << "(" << real; if (imag >= 0)cout << ",";
    else cout << ",";
    cout << imag << "i)" << endl;
}
//*/
Complex operator+(Complex &c1, Complex  &c2)
{
    return Complex(c1.real , c1.imag + c2.imag);
}
//*/
/*
Complex operator+(const Complex &c1, const  Complex  &c2)
{
    return Complex(c1.real , c1.imag + c2.imag);
}
*/
int main()
{
    Complex c1(3, 5), c3; 
    float i = 5;
    c3 = i + c1; 
    c3.display();
    return 0;
}

 

1.隐式转换不会被用于非const的引用参数

因为非const的引用只能绑定同类型的对象,const则可以绑定能互相转换的类型。即隐式转换不会被用于非const的引用参数。

2.二义性问题

1)当上述代码,形参为非const引用时,i为double类型,要调用运算符重载函数,i需要转换为Complex类型引用,那么就创建了临时变量,这样就不能调用运算符重载函数了,因此这种情况是c1先调用类型转换函数,使c1变成double类型,然后两个double类型相加,再调用转换构造函数,使右值变为Complex类型,过程为   c3.Complex((c1.operator double(c1))+i) 没有二义性。

2)当使用注释行,形参为const引用时,二义性。第一种同上,第二种,先使i转换为Complex类型,然后调用重载的+运算符,再赋值给c3,过程为 c3.Complex(operator+(Complex(i),c1)) 看到了二义性。

编译器会报:

yinshizhuanhuan.cc:40: ambiguous overload for `float& + Complex&' operator
yinshizhuanhuan.cc:40: candidates are: operator+(float, double) <built-in>
yinshizhuanhuan.cc:32: Complex operator+(const Complex&, const Complex&)

3)其他地方看到的通俗理解,感觉没毛病。如果输入的是const引用,const代表的是“不会被修改”,不会被修改也就等于没有输出,因此输入字符数组可以转换为一个临时的字符串再提供const引用,因为“转换得到的这个临时变量在函数退出时直接销毁就行,不会有任何问题。但如果输入的不是const引用,那就代表“会被修改”,也就等于有输出,那么字符数组是可以转换为字符串,但这个临时字符串在函数结束后该怎么办?你说它没有意义可以销毁,你怎么告诉编译器?编译器不知道,它就报错。

3.为什么用友元函数

《c++Primer中文版第四版》在p435页有指导原则,这样说:
IO操作符必须为非成员函数。我们不能将该操作符定义为类的成员,否则,左操作符将只能是该类类型的对象。
下标操作符必须为成员函数。
对称的操作符,如算数操作符/相等操作符/关系操作符/位操作符等,最好定义为普通非成员函数(友元)。

说明:

那我可以不定义转换操作符,执行单实参隐式转换,消除二义性

即:先使i转换为Complex类型,然后调用重载的+运算符,再赋值给c3,过程为 c3.Complex(operator+(Complex(i),c1))

代码如下:

#include<iostream>
using namespace std;
class Complex
{
public:
    Complex(){ real = 0; imag = 0; }
    Complex(float r){ real = r; imag = 0; }//转换构造函数 
    Complex(float r, float i) :real(r), imag(i){}
    //operator double() const { return real; } //转换操作符,必须为成员函数,形参列表为空,,一般不应该改变被转换的对象,通常定义为const
    //friend Complex operator+(Complex&, Complex&);//没有二义性  
    friend Complex operator+(const Complex&,const Complex &);//注释上边一句换为此句存在二义性,用显示转换消除二义性 20200302需注释转换操作符,执行单实参隐式转换,消除二义性20200302
    void display();
private:
    float real;
    float imag;
};
void Complex::display()
{
    cout << "(" << real; if (imag >= 0)cout << ",";
    else cout << ",";
    cout << imag << "i)" << endl;
}
/*
Complex operator+(Complex &c1, Complex  &c2)
{
    return Complex(c1.real , c1.imag + c2.imag);
}
*/
//*/
Complex operator+(const Complex &c1, const  Complex  &c2)
{
    return Complex(c1.real , c1.imag + c2.imag);
}
//*/
int main()
{
    Complex c1(3, 5), c3;
    float i = 5;
    c3 = i + c1;
    c3.display();
    return 0;
}
 
喜欢 3
分享