我们可以通过以下两种方式来声明一个从类型F到T的隐式转换:
- 在类T中声明一个仅接受一个类型为F的参数的构造函数(这其中也包括了那些使用了缺省参数值的构造函数,如:T::T(F,int i = 0)).
- 在类F中声明一个operator T的转换函数。
如果在函数调用中无法找到形参与实参完全匹配的函数声明,编译器就将试图通过在实参上进行隐式类型转换来找到合适的函数。对于作用域内所有的同名函数,编译器会试着将用户自定义的(至多一个)隐式转换作用到每个实参之上,来寻找匹配度最高的函数声明。如果编译器找到这样的一个函数,那么它会在目标代码中使用这个转换,最后调用该函数;否则编译器会向外报一个编译错误。
带有单个参数的构造函数
带有单个参数的构造函数同时也是一个隐式类型转换,这个事实让人觉得很不方便,因为我们希望的是该构造函数应该用于明确的对象创建过程中。只有在概念上将相同的信息由一种格式转换为另外一种的前提下,我们才允许有这样的隐式转换。如果在概念上,被转换后的对象和构造函数的参数不同, 就会让人费解。
class String {
//忽略此处细节
public:
String(const char* = "");
};
void print_heading(const String&);
//...
print_heading("Annual Report");
在调用print_heading时,我们会得到一个隐式的从char*到String的转换,由于String和char*在概念上来说是同一抽象模型的两个不同表示方法,所以这样的操作是合理的。
类型转换操作符
如果一个类中有多个类型转换操作符会导致编译期的二义性,例如:
上面的代码可以正常工作,代码中的String对象会被转换为const char* 用于输出。class String { char* rep; public: String(const char* = ""); operator const char*()const{return rep;} }; main(){ String s("hello word"); cout<<s<<endl; }
如果再增加一个类型转换操作符
新增的转换操作符可以将String 用于那些需要int的场合,但是他却破坏了我们已有的代码,使得编译器不知道执行哪种转换class String { char* rep; public: String(const char* = ""); operator const char*()const{return rep;} operator int ()const{return atoi(rep);} }; main(){ String s("hello word"); cout<<s<<endl; }