auto关键字的使用
auto
auto关键字是在编译期推到出类型然后替换,auto关键字不允许定义数组,不允许作为普通函数的参数,C++14之前不允许作为函数的返回值但是支持返回值后置,auto修饰的变量必须直接初始化,类内非const static成员变量不能用auto来修饰。
C++11提供了auto来自动推导类型,很大程度上提高了代码的直观性:
std::unordered_map<std::string, std::vector<int>> data_map;
// 不用auto
std::unordered_map<std::string, std::vector<int>>::iterator iter = data_map.begin();
// 使用auto推导
auto iter = data_map.begin();
但是auto的推到仍然引入了一些奇怪的问题,在C++中,类型说明符(如int),不仅代表着「类型」的含义还有「定义变量」的含义,auto可以视为一种带有类型推导的类型说明符,其本质仍然是类型说明符,所以,它同样承担了定义动作的任务,例如:
auto a = 1; //OKauto arr = {1, 3, 4}; //ERR,auto不允许定义数组
在C++14之前,auto关键字不允许推导函数返回值,但是支持返回值后置语法,所以在这种场景下,auto仅仅作为占位符,既不表示类型,也不表示定义变量,仅仅只是为了保证结构完整:
auto fun(int a) -> int; // () -> int表示定义函数,int表示函数返回值类型// C++14之后
auto Func(int cmd) {if (cmd > 0) {return 5; // 用5推导返回值为int}return std::string("123"); // ERR,返回值已经推导为int了,不能多类型返回
}
auto推导原则
同样还是出自这句话“auto是用来代替类型说明符的”,因此auto在语义上也更加倾向于“用它代替类型说明符”这种行为,尤其是它和引用、指针类型结合时,这种特性更加明显:
int a = 5;
const int k = 9;
int &r = a;
auto b = a; // auto->int
auto c = 4; // auto->int
auto d = k; // auto->int
auto e = r; // auto->int
可以看到,不论是用普通变量、只读变量、引用、常量去初始化auto变量时,auto都只会推导其类型,而不会带走其只读性,左右性这些内容。所以,auto的类型推导,并不是推导某个表达式的类型,而是去推导当前位置最合适的类型,采用「最简推导原则」。
比如说上面auto c = 4这里,auto可以推导为int,int &&,const int,const int &,const int &&,而auto选择的是里面最简单的那一种。
auto还可以跟指针符、引用符结合,而这种时候它还是满足上面“最简单”的这种原则,并且此时指的是“auto本身最简单”,举例来说:
int a = 5;
auto p1 = &a; // auto->int *
auto *p2 = &a; // auto->int
auto &r1 = a; // auto->int
auto *p3 = &p2; // auto->int *
auto p4 = &p2; // auto-> int **
p1和p2都是指针,但auto都是用最简原则来推导的,p2这里因为我们已经显式写了一个*了,所以auto只会推导出int,因此p2最终类型仍然是int *而不会变成int **。同样的道理在p3和p4上也成立。
auto& auto&&
auto&
当你需要直接修改原对象,或者希望避免不必要的拷贝时,可以选择auto&,auto&一般会推导为左值引用,当接受的类型为右值时会直接报错。同时,auto&不会忽略顶层const,因此如果初始值是一个常量,auto推导出的变量不是常量,你可以修改它。如果需要保留常量性,应使用 const auto。
auto&&
万能引用,会发生引用折叠,auto &&并不是要“定义一个右值引用”,而是“定义一个保持左右性的引用”,也就是说,绑定一个左值时会推导出左值引用,绑定一个右值时会推导出右值引用。不会忽略 const,不会推导出非引用。