介绍因为隐式类型转换而引起的一个非常隐晦的错误
下述代码的目的是设计一个简单的类A,希望A的对象在标准输出和文件输出时的表现不同
- #include <iostream>
- #include <fstream>
- using namespace std;
- class A
- {
- public:
- int a, b;
- A(int n1 = 0, int n2 = 0): a(n1),b(n2) {} // 加上explicit,阻止隐式转换,则代码没问题;
- };
- istream& operator>>(istream& in, A& a)
- {
- in >> a.a >> a.b;
- return in;
- }
- ostream& operator<<(ostream& out, const A& a)
- {
- out << a.a << " " << a.b << endl;
- return out;
- }
- ofstream& operator<<(ofstream& fout, const A& a)
- {
- fout << a.a << "xxx" << a.b << endl; // 如果注释掉这一句,则可以通过编译,说明问题出在这一句
- return fout;
- }
- void main()
- {
- A a;
- cin >> a;
- cout << a;
- ofstream ff("output.txt");
- ff << a;
- }
代码段中有语句 fout << a.a,我们相当然以为这段代码实际会调用ostream& operator<<(ostream &, int)函数,这个失误导致了上述错误代码的编写
实际上这句代码还可以与ofstream& operator<<(ofstream& fout, const A& a)匹配,因为fout是ofstream类型对象,a.a是int类型对象但是却可以隐式转换为类型A的一个临时对象参与函数调用
选择最佳匹配函数的规则如下:
1.最佳匹配函数的每个实参的匹配都不劣于其它可行函数需要的匹配
2.最佳匹配函数至少有一个实参的匹配优于其他可行函数需要的匹配
基于上述规则,编译器无法在两个候选匹配函数中找出最佳匹配的那个,所以会导致编译出现二义性错误
解决方法:
我们的解决办法肯定是要控制int类型对象隐式转换为类型A的一个临时对象参与函数调用
办法一:将A的构造函数声明为explicit,防止在隐式转换的上下文中使用构造函数
办法二:将函数ofstream& operator<<(ofstream& fout, const A& a)声明改为ofstream& operator<<(ofstream& fout, A& a),防止这个函数的第二个参数由int隐式转换成A的临时对象
注:非const的引用参数只能是相同类型,加上const才能接受"右值(right value)"引用
总结:重载函数时要注意由隐式类型转换引起的非常难以辨识出来的二义性错误