Chapter 2 auto
Item 5 Prefer auto to explicit type declarations
在定义变量时,相较于显式地指定其类型,使用 auto
有以下优点:
- 强制初始化
- 避免用于初始化的表达式类型和声明的类型不一致,从而导致可移植性或执行效率的问题
- 更改一处类型会自动更新和该类型有关的
auto
,便于重构 - 少打很多字
Item 6 Use the explicitly typed initializer idiom when auto deduces undesired types
在使用
auto
来定义变量时,如果用于初始化的表达式类型为 invisible proxy class,则类型推断的结果有可能和预想的不一致:c++1
2
3
4
5
6
7
8
9std::vector<bool> features() {
return std::vector<bool>(5, true);
}
cout << features()[3] << endl; // 1
auto a = features()[3];
cout << a << endl; // 0
bool b = features()[3];
cout << b << endl; // 1此处
std::vector<bool>::operator[]
的返回值类型是std::vector<bool>::reference
而非bool&
,前者即为一种 invisible proxy class。所谓 proxy class,是为了模拟或强化别的类型而设计的。有的 proxy class 对用户来说很明显,例如智能指针;而有的则设计得很隐晦,试图对用户封装这一细节,这类 proxy class 就称作 invisible proxy class。
std::vector<bool>::reference
就是其中之一,使用它是因为std::vector
对于bool
类型元素的存储方式是一个元素占一个 bit,而 C++ 不允许对 bit 的引用,那么operator[]
自然无法返回bool&
,只好返回一个表现得像bool&
的std::vector<bool>::reference
。正常情况下,用
std::vector<bool>::reference
初始化一个bool
类型的变量,会触发一次隐式转型,将转型后的值赋给该变量。而初始化auto
变量时,类型推断为std::vector<bool>::reference
,该变量的实际值依赖于具体实现细节,比如有可能是指向存储该 bit 的字节的指针以及一个字节内的偏移量,但是该函数的返回值是个右值,这条语句过后指针就悬空了,导致未定义的行为。对于如何判断函数的返回值是不是 invisible proxy class,没有什么太好的方法,书中给的建议是阅读文档和源代码。
需要注意的一点是,这并不是
auto
的问题,只是用户预期的类型和auto
推断出的类型不一致(auto
仍然遵守着它的规则),所以解决这一问题并不需要放弃auto
,而可以使用 explicitly typed initializer idiom,也就是将用于初始化的表达式的类型转换成用户想要的类型:c++1
auto a = static_cast<bool>(features(w)[5]);
此外,explicitly typed initializer idiom 并不是只有在遇到 invisible proxy class 才可以使用,当想要强调转型这一事实时也可以使用(毕竟 explicitly typed initializer idiom 本质上就是
static_cast
)