目录
前言
C++中的auto关键字是C++11标准中引入的一个新特性,旨在提高C++开发人员的开发效率和可读性。本文章将介绍auto关键字的用法,单纯作为笔记来使用,相关具体特性还需查阅官方.本文权当是自己的复习笔记了.注意,一下结论和编译器有关。
一、auto是什么?
我先来拿一张图来总体的简单介绍下suto到底是什么.
当在C++中使用auto
关键字时,它能够根据变量初始化的表达式自动推导出变量的类型。这样可以简化代码,提高可读性,同时还能让代码更加灵活,适用于不同类型的变量。auto
关键字的使用在C++11引入,之后的标准中也得到了进一步增强和扩展。
在C++中,变量声明通常需要显式指定其类型,例如int
、double
、char
等。而使用auto
关键字,可以让编译器根据初始化表达式自动推断变量的类型,无需手动指定。这使得代码更简洁、易读,并且在改变初始化表达式时不需要手动更改类型,大大减少了编写代码的工作量。
简单来说:C++11引入了auto
类型说明符,它允许在声明变量的时候根据变量初始化表达式的类型自动为变量选择匹配的类型。通过使用auto
,我们不需要手动指定变量的类型,而是让编译器来为我们推断变量的类型。
二、auto的相关规则
1.auto的使用规则
1.1auto
关键字必须初始化:
在使用auto
声明变量时,必须进行初始化,编译器通过初始化表达式推导出变量的类型。如果没有初始化,编译器无法确定变量的类型。
auto x = 42; // 正确,编译器将推导x的类型为int
auto y; // 错误,没有进行初始化,编译器无法推导y的类型
1.2auto
与const:
当使用auto
进行类型推导时,const修饰符会被保留。如果初始化表达式是const的,推导出的变量也会是const。
感谢@WRD_对于我的错误的指出。
在实验后得知
- const修饰与它紧邻的类型,优先修饰其左边的类型。
- auto在类型自动推导时,会在语法正确的前提下尽量少添加const。
这里分普通变量、引用、指针,三种情况分别讨论。
-
对于普通变量,auto的推导结果会忽略const修饰符。简单实验
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <memory>
#include <string>
template <typename T>
std::string type_name()
{
int status;
std::unique_ptr<char, void (*)(void *)> res{
abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status),
std::free};
std::string name = (status == 0) ? res.get() : typeid(T).name();
if (std::is_const_v<std::remove_reference_t<T>>)
{
name += " const";
}
return name;
}
int main()
{
const int x = 0;
auto y = x; // y -> int 没有保留const
const auto z = x; // z -> const int 额外增加了const
std::cout << "x is of type: " << type_name<decltype(x)>() << std::endl;
std::cout << "y is of type: " << type_name<decltype(y)>() << std::endl;
std::cout << "z is of type: " << type_name<decltype(z)>() << std::endl;
return 0;
}
我们通过运行程序得知可以看到y的类型是int,忽略了const修饰。
-
对于引用,auto的推导结果会保留const修饰符。
const int x = 0;
auto &y = x; // y的类型是 const int & 保留了const
int a = 0;
auto &b = a; // b -> int &
const auto &c = a; // c -> const int &
可以看到,虽然a没有const修饰,但可以在变量c定义时额外增加const修饰。
-
对于指针,我们举一个最简单的例子:
int *a = 0;
const int *b = 0;
auto x = a; // x -> int*
auto y = b; // y -> const int*
可以看到,b的const修饰也保留到了y上。
经过上面三种情况的讨论,我们得到了结论:
用auto初始化的变量,普通变量的const修饰会忽略,而指针和引用的const修饰会保留。
1.3引用类型的推导:
当使用auto
声明引用类型变量时,需要显式使用auto&
或auto&&
。auto
默认推导出的是实际对象的类型,如果希望得到引用类型,需要使用引用修饰符。
int x = 42;
auto& ref_x = x; // 引用类型的推导
1.4自动类型推断发生在编译期:
auto
关键字的类型推断是在编译期进行的,因此使用auto
不会对程序的运行时效率造成影响。
1.5auto
是一个占位符:
在编译器期间,auto
会被真正的类型所替代。这使得代码在编写时更加灵活,并且编译器会根据初始化表达。
2Auto的推导规则
2.1
初始化表达式不同类型的推导:
当使用auto
声明的变量初始化表达式是不同类型的,推导出的变量类型将会是其公共基类。如果没有公共基类,则推导失败,编译器会报错。
class Base {};
class Derived : public Base {};
Base* ptr = new Derived();
auto x = ptr; // 推导为Base*类型
2.2auto
与引用折叠:
C++11引入了引用折叠规则,在使用auto
声明引用类型时,如果初始化表达式是引用类型,那么推导出的变量类型也是引用类型。
int x = 42;
auto& ref_x = x; // 推导为int&
2.3decltype(auto)
:
C++14引入了decltype(auto)
,它用于推导函数返回类型,并保留引用类型。
int x = 42;
decltype(auto) getX() {
return x; // 推导为int&
}
2.4初始化表达式为lambda表达式:
当初始化表达式是一个lambda表达式时,auto
会推导出与lambda表达式的返回类型相同的变量类型。
auto func = [](int x) { return x * x; };
//在上述例子中,func的类型将根据lambda表达式的返回类型进行推导。
总之,auto的推导规则非常灵活,可以根据不同的目标对象类型推导出不同的类型。
3auto的'限制'
3.1无法用于非静态成员变量:
auto关键字不能用于声明类的非静态成员变量,因为非静态成员变量必须在类定义时就确定类型。
#include <iostream>
class MyClass {
public:
int x;
// auto y; // 错误,auto不能用于非静态成员变量
};
int add(auto a, auto b) { // 错误,auto不能用于函数参数
return a + b;
}
auto globalVar = 42; // 错误,auto不能用于全局变量
int main() {
int arr[] = {1, 2, 3, 4, 5};
auto sum = 0; // 推导为int类型
for (auto num : arr) {
sum += num;
}
std::cout << sum << std::endl;
return 0;
}
在这个示例中,我们尝试在类的非静态成员变量、函数参数以及全局变量的声明中使用auto关键字,但这是不被允许的。同时,我们在局部变量的声明中使用了auto,它可以正常推导为int类型。输出结果将是:15。
3.2不适用于函数参数和全局变量:
auto关键字只能在局部变量的声明中使用,不能用于函数参数和全局变量的声明。
3.3初始化表达式必须明确类型:
初始化表达式必须是能够明确推导出类型的,如果初始化表达式是表达式模板或需要运行时计算的,将无法使用auto。
3.4编译时类型推导:
auto关键字的类型推导是在编译时进行的,而不是在运行时。因此,编译器必须能够在编译阶段确定变量的类型。
三、自己总结平常用的多的
3.1 简化迭代器声明:
使用auto
关键字可以在迭代容器时,让编译器自动推导迭代器的类型,从而避免了手动书写复杂的迭代器声明。例如:
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
// 使用it进行迭代操作
}
3.2 简化复杂类型声明:
在涉及复杂类型的场景中,auto
关键字可以显著简化代码。例如,处理复杂的迭代器类型或嵌套的容器类型。
std::map<int, std::vector<std::string>> data;
// 复杂类型声明
std::map<int, std::vector<std::string>>::iterator it = data.begin();
// 使用auto简化类型声明
auto it = data.begin();
3.3泛型编程:
auto
在泛型编程中非常有用,特别是在处理复杂的模板类型时,它使得代码更加通用且不受特定类型的限制。
template <typename T, typename U>
auto multiply(T a, U b) -> decltype(a * b) {
return a * b;
}
ps 上面这个我也不是满会用 www
3.4适用于复杂模板类型:
在处理模板类型时,auto
可以让代码更加简洁清晰,特别是在涉及C++11之后的新特性如decltype
和std::result_of
时。
template <typename T>
auto getValue(T container, size_t index) -> decltype(container[index]) {
return container[index];
}
学习c++道阻且长啊