使用auto在编译期就可以推导出变量或表达式的类型。auto并不代表一个实际的类型声明,只是一个类型声明的“占位符”。
auto的推导规则:
- auto的使用必须马上初始化,否则无法推导出类型。
#include <iostream> int main() { auto a; //ERROR 无法推导auto类型,需要初始值设定 auto b = 1; //OK b的类型是int return 0; }
- auto在一行定义多个变量时,各个变量的推导结果必须一致,否则无法通过编译。
#include <iostream> int main() { auto i = 0, & b = i, * c = &i; //OK i的类型为int, b是i的引用,c是指向i的指针 auto d = 1, e = 1.0; //ERROR 对实体e的auto类型是double,但之前默示为int return 0; }
- auto不能用作函数的参数。
#include <iostream> void func(auto argv) {} void func1(auto argv = 1) {} int main() { return 0; }
函数func和func1都是无法通过编译的。
- auto不能用作非静态成员变量。
以下摘自《Primer c++ 第5版》:通常情况下,类的静态成员不应该在类的内部初始化。然而,我们可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是字面值常量类型的constexpr。初始值必须是常量表达式,因为这些成员本身就是常量表达式,所以它们能用在所有适合于常量表达式的地方。struct Foo { static constexpr int a = 1; //OK static constexpr double b = 1.0; //OK static constexpr auto c = 2; //OK auto d = 3; //ERROR };
这里说明的例子,在类中成员变量的初始化如果要使用auto,这是编译器的语法规则。不过笔者认为,应该尽量在类外初始化。当然也有例外,比如,《Primer c++ 第5版》书中提到的,如果某个静态成员的应用场景仅限于编译器可以替换它的值得情况。
- auto不能定义数组。
#include <iostream> int main() { int arr1[3] = { 0 }; //OK auto p = arr1; //OK : p -> int * auto arr2[2] = { 0 }; //ERROR return 0; }
- auto无法推导出模板参数。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec{ 0 }; //OK vector<auto> vec1{ 0 }; //ERROR return 0; }
-
auto与引用、cv限定符(const volatile)
const volatile在和auto的结合使用时效果一样。以下以const为例说明#include <iostream> int main() { int a = 1; auto& b = a; //OK : b -> int const auto f = a; //OK : f -> const int auto g = f; //OK : g -> int const auto& d = a; //OK : d -> const int& auto& e = d; //OK : e -> const int& return 0; }
1) 当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致。
2) 当声明为指针或引用时,auto的推导结果将保持初始化表达式的cv属性。 -
什么时候使用auto?
有时候我们并不关心具体类型,或者这个类型书写很长以至于影响到代码的阅读,我们可以使用auto来简化代码,在使用STL时,经常会遇到这样的情况、lambda表达式的类型等。但是一些简单的基本类型,建议还是明确写出来,意图明确维护性也好。
auto是一个很强大的工具,但任何工具都有两面性。不加选择的任意使用auto,会带来代码可读性和维护性的下降。
参考:《Primer c++ 第5版》《深入应用c++11代码优化与工程级应用》