C++11之前,已经支持显式转换操作符
#include <iostream>
using namespace std;
template <typename T>
class Ptr {
public:
Ptr(T* p): _p(p) {}
operator bool() const {
if (_p != 0)
return true;
else
return false;
}
private:
T* _p;
};
int main() {
int a;
Ptr<int> p(&a);
if (p) // 自动转换为bool型,没有问题
cout << "valid pointer." << endl; // valid pointer.
else
cout << "invalid pointer." << endl;
Ptr<double> pd(0);
cout << p + pd << endl; // 1,相加,语义上没有意义
}
显式转换操作符被滥用了。
在C++11中,标准将explicit的使用范围扩展到了自定义的类型转换操作符上,以支持所谓的“显式类型转换”。explicit关键字作用于类型转换操作符上,意味着只有在直接构造目标类型或显式类型转换的时候可以使用该类型。class ConvertTo {};
class Convertable {
public:
explicit operator ConvertTo () const { return ConvertTo(); }
};
void Func(ConvertTo ct) {}
void test() {
Convertable c;
ConvertTo ct(c); // 直接初始化,通过
ConvertTo ct2 = c; // 拷贝构造初始化,编译失败
ConvertTo ct3 = static_cast<ConvertTo>(c); // 强制转化,通过
Func(c); // 拷贝构造初始化,编译失败
}
// 编译选项: g++ -std=c++11 3-4-3.cpp
我们定义了两个类型ConvertTo和Convertable,Convertable定义了一个显式转换到ConvertTo类型的类型转换符。那么对于main中ConvertTo类型的ct变量而言,由于其直接初始化构造于Convertable变量c,所以可以编译通过。而做强制类型转换的ct3同样通过了编译。而ct2由于需要从c中拷贝构造,因而不能通过编译。此外,我们使用函数Func的时候,传入Convertable的变量c的也会导致参数的拷贝构造,因此也不能通过编译。
可以看到,所谓显式类型转换并没完全禁止从源类型到目标类型的转换,不过由于此时拷贝构造和非显式类型转换不被允许,那么我们通常就不能通过赋值表达式或者函数参数的方式来产生这样一个目标类型。通常通过赋值表达式和函数参数进行的转换有可能是程序员的一时疏忽,而并非本意。那么使用了显式类型转换,这样的问题就会暴露出来,这也是我们需要显式转换符的一个重要原因。