文章目录
- auto
-
- [优先选用 auto,而非显式型别声明](#优先选用 auto,而非显式型别声明)
- [当 auto 推导的型别不符合要求时,使用带显式型别的初始化物习惯用法](#当 auto 推导的型别不符合要求时,使用带显式型别的初始化物习惯用法)
auto
优先选用 auto,而非显式型别声明
用 auto 声明的变量,其型别推导都推导自其初始化物,所以它们必须初始化。

当 auto 声明变量却未初始化时,会编译错误。
用 auto 声明变量时可以简化代码。
cpp
template<typename It>
//dwim(做我想做,do what i mean)算法
//应用范围是从b到e的所有元素
void dwim(It b,It e){
while(b!=e){
typename std::iterator_traits<It>::value_type
currValue=*b;
//...
}
}
//用auto简化
template<typename It>
//dwim(做我想做,do what i mean)算法
//应用范围是从b到e的所有元素
void dwim(It b,It e){
while(b!=e){
auto currValue=*b;
//...
}
}
cpp
//不推荐
std::function<bool(int)> func = [](int x) { return x > 0; };
//推荐
auto func = [](int x) { return x > 0; };
用 auto 可以优化闭包声明。
用 auto 声明闭包比 function 性能更优越。
| 特性 | auto 声明闭包 | std::function 声明 |
|---|---|---|
| 内存 | 类型和闭包类型一样,仅占用闭包捕获变量所需的空间 | 类型为 function,存储 function 实例,内存对齐 function 类型,通常会有堆内存分配 |
| 速度 | 编译器可以进行内联优化 | 存在间接调用,难以内联 |
| 类型匹配 | 绝对精确匹配 | 可能由于类型不完全匹配导致隐式转换 |
用 auto 声明变量可以避免型别捷径。
cpp
vector<int>v;
//v.size()返回std::vector<int>::size_type类型,规定成一个无符号整数
unsigned sz=v.size();
在 32 位 windows 上,unsigned 和std::vector::size_type 尺寸是**一样**,但是在 64 位 windows 上,**前者为 32 位,后者为 64 位**,在 32 位的程序迁移到 64 位 Windows 上会出现异常,并且难以调试。
用 auto 则尺寸一致:
cpp
auto sz=v.size();//sz的型别为std::vector<int>::size_type
cpp
std::unordered_map<std::string,int>m;
for(const std::pair<std::string,int>&p:m){
//...
}
看似没什么问题,实际上 std::unordered_map 的**键值部分是 const 只读的,即类型型别为 std::pair<const std::string,int>,而当该类型赋值到类型为 std::pairstd::string,int& 的变量时,它并不会随我们期待的那样: p 为 m 容器中值的引用**。而是先对 m 中的** 每个对象都做一次复制操作**,形成一个 p 想要绑定的型别的临时对象,然后把 p 这个引** 用绑定到该临时对象**。在**每次循环迭代结束时该临时对象会被析构一次**。
这样下来,不仅没有成功引用到 m 中的对象,多次复制和析构也会导致不必要的性能开销。而使用 auto 可以保证类型正确,消除影响。
cpp
std::unordered_map<std::string,int>m;
for(const std::pair<std::string,int>&p:m){
//...
}
当 auto 推导的型别不符合要求时,使用带显式型别的初始化物习惯用法
cpp
#include<vector>
class Widget {
public:
Widget(int id) : id(id) {}
private:
int id;
};
std::vector<bool> features(const Widget&w){
//...
}
void processWidget(const Widget&w,const bool isHigh){
//...
}
int main(){
Widget w(0);
bool highPriority1=features(w)[5];
processWidget(w,highPriority1);
//类型推导为std::vector<bool>::reference
auto highPriority2=features(w)[5];
//未定义行为
processWidget(w,highPriority2);
return 0;
}
为什么用 auto 推导出的 highPriority2 会出现未定义行为?
原因在于 std::vector[] 的返回值并不是我们对于该容器其他型别一样所期待的 bool&(C++中禁止返回比特的引用),而是**std::vector::reference**。该类型能够实现在所有能用 bool&的地方的保证它们也能用,因为该类型做了一个向** bool 的隐式型别转换**。
std::vector::reference 是一个代理类,智能指针也是代理类,区别在于后者是显式使用,而前者趋于隐式使用。我们知道的是智能指针通过内置一个指针指向特定对象来代理该对象,std::vector::reference 也是一样。
⚠️但是在调用 features 时会返回一个 std::vector型别的临时对象 temp,而highPriority2 则代理的是该临时对象 temp,在表达式结束时,temp 发生析构,则highPriority2 内置的指针为空悬指针,所以会导致未定义行为。
📢**解决方法**:**使用带显式型别的初始化物习惯用法**(强制进行另一次型别转换),这样就能使用 auto 推导接受。
cpp
auto highPriority2=static_cast<bool>(features(w)[5]);
这种习惯用法的应用并不限于会产生代理型别别的初始化物。它同样可以应用于你想要强调你意在创建一个型别有异于初始化表达式型别的变量的场合。
cpp
//假设有一个计算某容差值的函数
double calcEpsilon();//返回容量差
float ep=calcEpsilon();//隐式转换
auto ep=static_cast<float>(calcEpsion());//强制转换