Effective Modern C++读书笔记:auto(条款5-6)
条款5:优先选用auto,而非显示类型声明
1. 摆脱繁杂的类型声明
2.避免踩坑
1)使用auto与使用std::function声明闭包的对比
//使用std::function声明一个闭包
std::function<bool(const std::unique_ptr<Widget>&,
const std::unique_ptr<Widget>&)>
func = [](const std::unique_ptr<Widget>&p1,
const std::unique_ptr<Widget>&p2)
{return *p1 < *p2};
//使用auto声明一个闭包
auto func = [](const std::unique_ptr<Widget>&p1,
const std::unique_ptr<Widget>&p2)
{return *p1 < *p2};
使用auto除了声明上的方便,还有以下区别:使用auto声明的,存储该闭包的变量和该闭包是同一个类型,从而它要求的内存和闭包相同。而使用std::function声明的,存储闭包的变量只是std::function的一个实例,所以它具有固定的std::function类型的大小,而这个大小对闭包来说不一定够用,这种情况下,std::function的构造函数就会使用堆内存来使用该闭包。再有,编译器的实现细节一般都会限制内联,这种情况下会有函数调用的开销。
总结:std::function这种写法比auto来的 1.啰嗦 2.可能导致内存耗尽异常
2)类型窄化
考虑如下代码
std::vector<int> v;
...
unsigned size = v.size();
标准规定:v.size()
返回应该是std::vector<int>::size_type
,类型的不同可能会导致一些难以预料的错误。如在32位windows上,std::vector<int>::size_type
与unsigned
尺寸是一样的,而在64位windows上,unsigned
是32位,而std::vector<int>::size_type
是64位,这样可能会导致一些很难排查的异常。
而使用auto则会有效避免这个可能的错误
3)unordered_map
考虑如下代码:
std::unordered_map<std::string, int> m;
//some work to do
for(const std::pair<std::string. int> &p:m){
//some work to do
}
想要发现上述代码的异常,就要记住std::unordered_map的键值部分是const,也就是m中存储的是std::pair<const std::string,int>
,而不是std::pair<std::string, int>
。上面的声明错误会使编译器想方设法将std::pair<const std::string,int>
转化为std::pair<std::string, int>
,它找到的方法是对m中的每个对象都做一次复制操作,把m中的元素复制到一个临时对象上,再把p绑定到这个临时对象上,在循环的每次迭代结束时,会再调用析构函数。除了调用构造函数和析构函数的开销之外,另一个可能引起灾难性后果的是对p取址,那么只是对临时对象取址,这可能会导致程序直接崩溃!
条款6:auto可能导致的不符合预期行为
auto返回隐形的代理类型导致不符合预期
考虑如下代码:
vector<bool> features();
auto ret = features()[5];
这里ret的类型并不是预期的bool类型,而是一个对用户隐形的std::vector<bool>::reference
类型。这是因为vector<bool>
的operator []
的返回值并不是bool,而是一个代理类型:std::vector<bool>::reference
,而这可能会引起错误,严重甚至可能会产生空悬指针导致程序崩溃。
详细原因请参见Effective Modern C++ P47
再考虑如下代码:
vector<bool> features();
bool ret = features()[5];
上面的代码会使std::vector<bool>::reference
隐示转化为bool对象,从而使后面的调用符和预期。
一个普遍的规律是:"隐形代理类"和auto无法和平共处。
推荐阅读
-
Effective Modern C++ 条款32 对于lambda,使用初始化捕获来把对象移动到闭包
-
Effective Modern C++ 条款23 理解std::move和std::forward
-
Effective Modern C++ 条款37 在所有路径上,让std::thread对象变得不可连接(unjoinable)
-
Effective Modern C++ 条款38 意识到线程句柄的析构函数的不同行为
-
Effective Modern C++ 条款22 当使用Pimpl Idiom时,在实现文件中定义特殊成员函数
-
Effective Modern C++读书笔记:auto(条款5-6)
-
读书笔记《Effective C++》条款21:必须返回对象时,别妄想返回其reference
-
读书笔记《Effective c++》 条款21 必须返回对象时,别妄想返回其reference
-
《Effective Modern C++》学习总结(条款6- 10)
-
Effective Modern C++ 条款17 理解特殊成员函数的生成