C++允许编译器在不同类型之间执行隐式类型转换(implicit conversions)。
两种函数允许编译器执行这样的转换:
- 单自变量constructors
- 隐式类型转换操作符
单自变量constructors
单自变量constructors是指能够以单一自变量成功调用的constructors。如此的constructor可能拥有单一参数,也可能声明拥有多个参数,并且除了第一个参数之外都有默认值。下面给出其这两种形式:
class Name
{
public:
Name(const string& s); // 可以把string转换成Name
};
class Rational
{
public:
Rational(int numerator = 0, int denominator = 1); // 可以把int转换成Rational
}
对于如下调用:
void TestFunc(Name myName)
{
...
}
string strName;
TestFunc(strName); // 调用成功,编译器调用了单参数的构造函数
编译不会报错,如果需要禁止这种行为,可以在单参数构造函数前加上关键字explicit即可。
class Name
{
public:
explicit Name(const std::string& s); // 禁止调用进行隐式类型转换
};
隐式类型转换操作符
隐式类型转换操作符是关键字operator之后加上一个类型名称。不需要指定返回值类型,因为其返回值类型基本上已经表现在函数名称上。
class Rational
{
public:
...
operator double() const; // 将Rational转换为dobule
}
这个函数会在下面代码中自动调用:
Rational r(1, 2);
double d = 0.5 * r; // 将r转换成double, 然后执行乘法运算
接下来解释为什么最好不要提任何类型转换函数。根本原因在于,在你从未打算也未于其的情况下,此类函数可能会被调用,而其结果可能是不正确的、不直观的程序行为,很难调试。
例如,对于如下调用:
Rational r(1, 2);
cout << r;
因为没有对Rational实现operator << , 所以预期会编译报错,但是结果确实正常运行,因为r会被编译器进行隐式类型转换成double类型。这也显示:隐式类型转换,它们的出现可能导致错误(非预期)的函数被调用。
解决的办法是以功能对等的另外一个函数取代类型转化操作符。
例如为了允许将Rational转换为double,可以以一个名为asDouble的函数取代operator double。
class Rational
{
public:
...
double asDouble() const; // 将Rational转换为double
};
此后member function必须被明确调用:
Rational r(1, 2);
cout << r; // 错误,Rational没有operator <<。
cout << r.asDouble(); // 可以以double形式输出
大部分时候,"必须明白调用类型转换函数"虽然带来了些许不便,却可以为"不再默默调用那些其实并不打算调用的函数"而获得弥补。这也是为什么不存在从string object至C-style char*的隐式转换函数的原因。而是提供了一个显示的member function c_str()来执行上述转换行为。