Effective c++学习笔记 (item15) 扩展 讨论
讨论隐式转换
c++的隐式转换其实就是编译器发现本来某个位置期待的类型是A, 但是代码却给了类型B, 这种情况下,编译器会悄悄地调用隐式转换,让编译能够进行下去。好处是代码上比强制转换看起来更简洁更优雅。坏处就是编译器悄悄做的事情有可能是你不希望的。
内置类型的隐式转换是编译器自带的。比如int到long,或者long到int,int 到bool等等的转换规则是编译器自带,用户无法自定义的。
类对象的隐式转换是用户可以自定义的。
例子1:
比如下面的例子
class AClass
{
};
class BClass
{
public:
string name;
};
void printBClass(BClass tt)
{
std::cout << tt.name << std::endl;
}
int main()
{
AClass a;
printBClass(a);
}
编译器知道printBClass的入参应该是BClass类型,但是代码中给的a却是AClass类型. 所以编译器会悄悄地尝试执行printBClass(BClass(a)); 于是编译器会去找BClass(a)的实现. BClass(a)可以有两种理解, 一种理解是BClass类型的拷贝构造函数. 此时编译器会去BClass类中找是否有这种拷贝构造函数. 本例中没有. 另一种理解是a对象的操作运算BClass(), 此时编译器会去AClass中去找是否有类似的运算符定义. 本例中也没有.所以编译会报错,并中断.
我们可以修改下例子, 通过自定义BClass(AClass a)的构造函数,来适配第一种理解:
class AClass
{
};
class BClass
{
public:
BClass(AClass a)
{
name = "隐私转换来自BClass的构造函数";
}
string name;
};
void printBClass(BClass tt)
{
std::cout << tt.name << std::endl;
}
int main()
{
AClass a;
printBClass(a);
}
我们也可以修改下例子,通过自定义AClass的运算符BClass(),来适配第二种理解
class BClass
{
public:
string name;
};
class AClass
{
public:
operator BClass()
{
BClass tmp;
tmp.name = "隐私转换来自AClass的运算符操作";
return tmp;
}
};
void printBClass(BClass tt)
{
std::cout << tt.name << std::endl;
}
int main()
{
AClass a;
printBClass(a);
}
那如果我即定义AClass的运算符BClass(), 又定义了BClass(AClass a)的构造函数, 编译器就会疑惑不知道用那个去做隐式转换的规则, 会报错并中断.
例子2:
对于下例的if(a), 编译器期望if里面是个bool类型,但是代码给定的a却是的AClass类型,所以编译器会悄悄的尝试执行if(bool(a)), 同例子1相同, 编译器对bool(a)一样有两种解读. 但是bool是内置类型,不可能自定义bool类型的拷贝构造函数.所以对于这个例子,只能通过定义AClass的bool()运算符的方式实现隐式转换.
class AClass
{
public:
};
int main()
{
AClass a;
if (a)
{
;
}
}
只能通过定义AClass的bool()运算符的方式实现隐式转换
class AClass
{
public:
operator bool()
{
return true;
}
};
int main()
{
AClass a;
if (a)
{
std::cout << "自定义隐式转换成功" << std::endl;
}
}
参考如下文章:
彻底理解c++的隐式类型转换 - apocelipes - 博客园 (cnblogs.com)
Overload resolution - cppreference.com
Implicit conversions - cppreference.com