1. auto
auto 是C++11引入的关键字,用于进行自动类型推导。使用 auto 可以在变量声明时省略类型,让编译器根据初始化表达式自动推导变量的类型。这使得代码更加简洁、可读性更高,并且在使用范围较大的情况下,可以提高代码的可维护性。
1.1 使用规则
语法为:
auto 变量名 = 变量值;
简单的例子:
auto x = 3.14; // x 是浮点型 double
auto y = 520; // y 是整形 int
auto z = 'a'; // z 是字符型 char
auto nb; // error,变量必须要初始化
auto double nbl; // 语法错误, 不能修改数据类型
auto pi = new auto(42); // pi 的类型被推导为 int*
const auto& y = x; // y 的类型被推导为 const int&
在这些例子中,编译器会根据初始化表达式的类型来推导变量的类型。
1.2 注意事项
1. 首先就是在使用规则中可以看到的,使用auto的时候必须要对变量进行初始化,否则会报错。原因在于:C++11中auto并不代表一种实际的数据类型,只是一个类型声明的 占位符。
2. 不能作为函数参数使用。因为只有在函数调用的时候才会给函数参数传递实参,auto要求必须要给修饰的变量赋值,因此二者矛盾。
int func(auto a, auto b) // error
{
cout << "a: " << a <<", b: " << b << endl;
}
3. 不能使用auto关键字定义数组
int func()
{
int array[] = {1,2,3,4,5}; // 定义数组
auto t1 = array; // ok, t1被推导为 int* 类型
auto t2[] = array; // error, auto无法定义数组
auto t3[] = {1,2,3,4,5};; // error, auto无法定义数组
}
4. 无法使用auto推导出模板参数
template <typename T>
struct Test{}
int func()
{
Test<double> t;
Test<auto> t1 = t; // error, 无法推导出模板类型
return 0;
}
1.3 应用场景
1. 范围循环
auto 在范围循环中也非常有用,它可以自动推导集合中元素的类型。
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (const auto& num : numbers) {
std::cout << num << std::endl; // num 的类型被推导为const int&
}
2. lambda表达式
auto add = [](int a, int b) -> int {
return a + b;
};
3. 结构化绑定(c++17新特性)
在C++17中,auto 还可以与结构化绑定(Structured Binding)一起使用,用于将结构体或数组的成员绑定到自动推导的变量上。
std::pair<int, double> myPair = std::make_pair(42, 3.14);
auto [first, second] = myPair; // 结构化绑定,first 和 second 的类型被推导为 int 和 double
总的来说,auto 的使用使得C++代码更加灵活,尤其在处理模板、泛型编程以及复杂的数据结构时,可以减少代码的重复性,提高代码的可读性和可维护性。
2. decltype
在某些情况下,不需要或者不能定义变量,但是希望得到某种类型,这时候就可以使用C++11提供的decltype关键字了,它的作用是在编译器编译的时候推导出一个表达式的类型,语法格式如下:
decltype (表达式)
decltype 是“declare type”的缩写,意思是“声明类型”。decltype的推导是在编译期完成的,它只是用于表达式类型的推导,并不会计算表达式的值。
2.1 获取变量和表达式的类型
int a = 10;
decltype(a) b = 99; // b -> int
decltype(a+3.14) c = 52.13; // c -> double
decltype(a+b*c) d = 520.1314; // d -> double
2.2 获取函数返回类型
int func();
decltype(func()) value; // value 的类型被推导为 func() 函数的返回类型
在这个例子中,decltype(func()) 返回函数调用 func() 的返回类型,并将其用于声明变量 value。
2.3 结构化绑定(c++17新特性)
结构化绑定中,你可以使用 decltype 关键字获取绑定变量的类型,特别是当你不确定结构体成员的类型时:
struct Point {
int x;
int y;
};
Point p = {1, 2};
auto [x, y] = p; // 结构化绑定,x 和 y 的类型被推导为 int
decltype(x) newX = 10; // newX 的类型被推导为 int,与 x 的类型相同
decltype(y) newY = 20; // newY 的类型被推导为 int,与 y 的类型相同
2.4 泛型编程
在泛型编程中,decltype 可以帮助你获取模板参数的类型。
template <typename T>
void process(T value) {
decltype(value) newValue; // newValue 的类型与 value 的类型相同
// 处理代码
}
在这个例子中,decltype(value) 的类型被推导为模板参数 T 的类型。
这些decltype的例子暂时用的不多(我用得不多),目前就只在lc刷题的时候,定义小顶堆的时候才用到,所以我把定义小顶堆的方法放在这:
#include <iostream>
using namespace std;
#include <queue>
void test01()
{
auto cmp = [](const int& a, const int& b) {
return a > b;
};
priority_queue<int, vector<int>, decltype(cmp)> pq(cmp);
pq.push(2);
pq.push(23);
pq.push(6);
pq.push(52);
pq.push(1);
pq.push(10);
pq.push(29);
pq.push(62);
cout << pq.top() << endl; // 1
pq.pop();
cout << pq.top() << endl; // 2
}
int main()
{
test01();
return 0;
}
当然也有别的方法,我习惯这么定义。
总的来说,decltype 提供了一种非常灵活的方式来获取表达式、变量、函数调用的类型,使得C++代码在处理泛型编程、模板元编程和复杂类型推导时更加方便和可读。
3. 返回类型后置
返回类型后置(Trailing Return Type)是C++11引入的特性,它允许在函数定义时将返回类型放在参数列表之后,使用 -> 关键字指定返回类型。这个特性主要用于函数返回类型依赖于参数类型或表达式结果的情况。返回类型后置的语法结构如下:
auto function_name(parameters) -> return_type {
// 函数体
}
其中,function_name 是函数的名称,parameters 是函数的参数列表,return_type 是函数的返回类型。
以下是返回类型后置的使用场景和示例:
3.1 函数返回类型依赖于参数类型
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}
在这个例子中,add 函数接受两个参数,参数的类型可能是任意类型。返回类型使用 decltype 关键字,根据参数的类型推导出实际的返回类型,这样函数就可以处理不同类型的参数。
3.2 函数返回类型依赖于表达式结果
auto divide(int a, int b) -> double {
if (b != 0) {
return static_cast<double>(a) / b;
} else {
throw std::invalid_argument("Division by zero");
}
}
在这个例子中,divide 函数接受两个整数参数 a 和 b,返回类型是 double。返回类型后置允许函数的返回类型根据条件语句内的分支进行推导。
3.3 Lambda 表达式中的返回类型后置
auto lambda = [](int x, int y) -> int {
return x * y;
};
在Lambda表达式中,返回类型后置允许你明确指定Lambda函数的返回类型。
返回类型后置的主要优势在于它使得函数的返回类型更清晰,尤其在处理泛型编程时,可以使代码更具灵活性。此外,返回类型后置还可以用于处理递归函数的情况,因为在函数体内可以直接使用参数,而不用担心返回类型的声明在函数体之前。