C++隐式转换的那些事
昨天在公司干活的时候遇到一个C++隐式转换的问题, 折腾了很久, 后来碰巧发现了一丝线索, 才得以解决. 废话不多说了, 先上代码:
// 此数组模板类声明
template
class SomeArray {
SomeArray(int physicalLength = 32, int growLength = 32);
...
int append(const T& value);
AcArray& append(const AcArray& otherArray);
...
};
// 枚举类型
enum Format { kFirst, ..., kLast };
// 问题代码
SOME_FUNC(...) {
...
SomeArray<Format> fmtArray;
...
for (int fmt = kFirst; fmt <= kLast; ++fmt) {
if (fmt satisfy some condition) fmtArray.append(fmt);
}
}
上述问题代码在执行循环的时候, 明明append元素这条语句执行了, 但是fmtArray仍然为空. 于是我就在这条语句处设置 了断点, 然后F11进去看. 结果发现跳转到了SomeArray的构造函数里. 然后我又尝试了几次, 发现还是如此. 我就纳闷, 难道我构建出来的二进制出错导致link不对? 不可能啊, 这是一个模板类, 不需要link啊. 又或者是因为这个模板类是编译在一个pch文件里, 那个文件被弄dirty了? 尝试了一下, 发现不是由于link或者pch导致的. 没办法了, 下班前, 开始做了一个fully clean build(2 ~ 3小时), 希望排除某些模块的dirty所导致的问题.
今天上班后, 和同事J继续调试, 发现, 居然还是继续跳入构造函数. 构造函数调用完毕后, 退回到append元素语句处. 这时候奇迹发生了: 同事J手一不小心, 又按了一下F11, 结果, 居然跳入了append函数内部. 这个append函数并不是append一个元素, 而是一个SomeArray<Format> 对象, 但是这个对象是空的. 执行完毕这个函数后, 继续按F11, 结果跳到了SomeArray的析构函数. 到这里, 问题基本上已经很清楚了:
由于fmt是一个整形值, 那么C++在解析重载append函数时, 由于Integer->Enumeration的转换不能自动进行, 所以编译器倾向于寻找一条更可行的转换链: int -> SomeArray<Format>, 因为默认构造函数支持这种转换. 于是, 你所append的每个元素, 都用来作为构建临时数组对象时所使用的physicalLength 参数值. 最终得到的fmtArray也因为append了一堆空数组而始终为空.
从这个问题, 我得到两个结论:
1. C++的类型转换的确诡异, 自己平时编写的时候, 一定要注意适当地使用explicit关键字,避免不必要的隐式转换.
2. 遇到诡异的事情, 还是要沉着冷静, 利用已有的信息去进行深入的分析, 不放过每个可疑的线索.
上一篇: js的类型转换那些事