最近项目中出现了隐式类型转换引起的BUG,使用关键字explicit解决了问题。本文使用简化后的例子说明该问题。
首先,定义一个分数类Fraction,它包含构造函数,将其转化为double类型的成员函数,并且重载了运算符+。
代码如下(该代码会有编译错误,先思考一下错在哪里)。
#include<iostream>
using namespace std;
class Fraction
{
public:
Fraction(int numerator, int denominator = 1)
:m_numerator(numerator), m_denominator(denominator)
{
}
operator double() const
{
return (double)m_numerator / m_denominator;
}
Fraction operator+(const Fraction &f)
{
return Fraction(m_numerator + this->m_numerator, m_denominator + this->m_denominator);
}
private:
int m_numerator; //分子
int m_denominator; //分母
};
int main()
{
Fraction obj(3, 5);
double sum = obj + 4;
std::cout << "sum = " << sum << std::endl;
return 0;
}
你想到了吗?没错!是代码具有二义性。出现二义性的代码是main()函数中一行语句:
Fraction sum = f + 4;
下面我将详细分析二义性的原因。当编译器看到上述代码时,它发现有两种可行的解释:(1)将4隐式转为Fraction(4)(该转换由构造函数实现),然后由于重载了运算符+,两个Fraction对象相加得到的对象Fraction对象,然后转化为double(该转换由函数double()实现);(2)将对象 obj 转换为double(该转换由函数double()实现),然后两个double相加得到一个double。
编译器在面对代码上述两种可行的解释时,彻底蒙圈,它不知道该选择哪种方法执行,因此向程序员报告二义性的错误,希望将问题交给聪明的程序员来解决。
面对这样的二义性,程序员该如何解决呢?没错,用explicit修饰构造函数,明确的告诉编译器,不希望将double隐式转换为Fraction类型的对象,第一种道路被封死,编译器只有第二条路可走。
修改后的构造函数如下,此时程序不再有编译错误。
explicit Fraction(int numerator, int denominator = 1)
:m_numerator(numerator), m_denominator(denominator)
{
}