auto其实不算一个新的关键字,只不过它的老的含义现在已经失去了意义。
旧版 auto 的含义
在C语言和C++98/C++03标准中,auto是一个存储类别说明符,用于显式地声明一个变量具有自动存储期。这意味着变量是在进入其作用域时创建,并在离开该作用域时销毁。这是大多数局部变量默认的行为,因此很少有人会显式地使用auto来声明变量,因为它几乎总是多余的。
例如:
void function() {
auto int x = 10; // 自动存储期,实际上等价于 int x = 10;
// ...
}
在这个例子中,x 是一个具有自动存储期的局部变量。但是,因为所有局部变量默认都是自动存储期的,所以这里的auto关键字是可选且冗余的。
C++11 及以后版本中的 auto
从C++11开始,auto的语义被完全改变,成为了一种类型推导机制。它允许编译器根据初始化表达式的类型自动推断变量的类型。这使得代码更加简洁,尤其是在处理复杂类型(如模板或迭代器)时。
例如:
std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
在这里,auto让编译器推断出it的类型应该是std::vector<int>::iterator,从而避免了显式写出这个较长的类型名。
我们再看题目中提到的问题:
int i = 8;
int& j = i;
auto m = j;
j是 int& 类型,那么m是什么类型呢?也是int&类型吗?
答案是:m在这里是int类型而不是int& 引用类型。
auto不会保留引用特性。也就是说,auto会将j解引用,得到它所指向的实际对象的类型。
我们代码实际操作验证一下:
修改后再实验:
输出还是int类型,这是怎么回事呢?
typeid(decltype(m)).name() 返回的类型名可能不是特别直观,因为它依赖于编译器的具体实现。不同的编译器可能会以不同的格式返回类型名。例如,在某些编译器中,int 类型可能显示为 i,引用类型可能带有额外的修饰符(如 R 表示引用)。如果需要更可读的输出,可能需要自己解析这些符号,或者仅在调试和开发过程中使用它们。
为了更明确地确认 m 的类型是否为 int&,可以使用类型特征(type traits)来检查。例如:
int i = 8;
int& j = i;
auto m = j;
std::cout << "Type: " << typeid(decltype(j)).name() << std::endl;
if (std::is_same<decltype(m), int&>::value) {
std::cout << "m is of type int&" << std::endl;
}
else {
std::cout << "m is not of type int&" << std::endl;
}
运行:
修改后:
int i = 8;
int& j = i;
auto& m = j;
std::cout << "Type: " << typeid(decltype(j)).name() << std::endl;
if (std::is_same<decltype(m), int&>::value) {
std::cout << "m is of type int&" << std::endl;
}
else {
std::cout << "m is not of type int&" << std::endl;
}
输出:
图片
这种方法可以在编译时进行类型检查,并且不会依赖于编译器的具体实现。
通过上面的测试可以得到:不加引用,m在这里是int类型而不是int& 引用类型,auto不会保留引用特性。
还有同样的 auto也不会保留const特性。
int main()
{
const int i = 9;
auto j = i;
if (std::is_same<decltype(j), const int>::value) {
std::cout << "j is of type const int" << std::endl;
}
else {
std::cout << "j is not of type const int" << std::endl;
}
return 0;
}
运行输出:
图片
总结:
auto不会保留引用特性。
auto不会保留const特性。