C++11新特性 4.auto自动类型推导、decltype

目录

一.简介

1.基本概念

2.语法

二.使用示例

示例1:基本类型推导

示例2:带const 、volatile限定符

示例3:迭代器类型推导(常用)

示例4:函数返回值类型推导

示例5:用于 Lambda 表达式

三.注意事项

1.函数形参,没有初始化

2.不能用于类的非静态成员初始化,没有对象无法推导

3.不能使用 auto 关键字定义数组

4.无法使用 auto 推导出模板参数

四.补充:decltype

1.推导规则:

2.典型应用

泛型编程中的应用:

返回值类型后置:

一.简介

1.基本概念

在 C++11 中,auto 关键字被赋予了新的语义。在旧标准里,auto 用于声明自动存储期的变量,不过使用得较少且意义不大。而在 C++11 及以后,auto 主要用于自动类型推导。它允许编译器根据变量的初始化表达式自动推断出变量的类型,从而简化代码编写,尤其是在处理复杂类型时,能让代码更加简洁易读。

2.语法

auto 变量名 = 变量值;

编译器会根据 初始化表达式 的类型来确定 变量名 的类型。auto 本身并非一种实际的类型,而是一个类型占位符,它会在编译时被替换为具体的类型。 

二.使用示例

示例1:基本类型推导

#include <iostream>

int main() {
    auto num = 10;  // 编译器根据 10 推断 num 的类型为 int
    auto pi = 3.14; // 编译器根据 3.14 推断 pi 的类型为 double
    std::cout << "Type of num: " << typeid(num).name() << std::endl;
    std::cout << "Type of pi: " << typeid(pi).name() << std::endl;
    return 0;
}

在这个例子中,num 被初始化为整数 10,编译器推断其类型为 intpi 被初始化为浮点数 3.14,编译器推断其类型为 double。 

示例2:带const 、volatile限定符

当变量不是指针或者引用类型时,推导的结果中不会保留volatile、const限定符

当变量是指针或者引用类型时,推导的结果中会保留volatile、const限定符

#include <iostream>

int main()
{
    //double
    auto x = 3.14;
    //int
    auto y = 520;
    //char
    auto z = 'a';

    int temp = 100;
    //int
    auto* a = &temp;
    //int*
    auto b = &temp;
    //int
    auto& c = temp; //取别名,指向同一个内存地址
    //int
    auto d = temp;

    //有const修饰
    int tmp = 250;
    //a1 : const int,auto:int
    const auto a1 = tmp;
    //当变量不是指针或者引用类型时,推导的结果中不会保留volatile、const限定符
    //a2:int
    auto a2 = a1;
    //a3 : const int
    const auto& a3 = tmp;
    //当变量是指针或者引用类型时,推导的结果中会保留volatile、const限定符
    //a4 : const int
    auto& a4 = a3;
    //pt4 : 保留,const int , 不能修改值
    auto* pt4 = &a1;

    return 0;
}

 代码中注释了推导类型和规则。

示例3:迭代器类型推导(常用)

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    // 使用 auto 简化迭代器类型的书写
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    return 0;
}

在 C++ 中,STL容器的迭代器类型通常比较复杂,使用 auto 可以避免手动书写冗长的迭代器类型,使代码更加简洁。 

示例4:函数返回值类型推导

#include <iostream>

auto add(int a, int b) {
    return a + b;
}

int main() {
    auto result = add(3, 5);
    std::cout << "Result: " << result << std::endl;
    return 0;
}

这里 add 函数使用 auto 作为返回值类型,编译器会根据 return 语句中的表达式 a + b 推断返回值类型为 int

示例5:用于 Lambda 表达式

#include <iostream>

int main() {
    auto lambda = [](int x, int y) { return x + y; };
    int sum = lambda(2, 3);
    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

Lambda 表达式的类型是编译器自动生成的匿名类型,使用 auto 可以方便地存储和使用 Lambda 表达式。

三.注意事项

1.函数形参,没有初始化

在函数形参列表中使用 auto 且没有初始化时是不被允许的。因为函数在定义时,编译器需要明确知道每个形参的具体类型,以便进行参数传递和类型检查等操作。而 auto 是依赖于初始化表达式来推导类型的,在函数形参这里没有合适的初始化过程来完成类型推导。

不过,在 C++14 及以后版本支持使用 auto 作为 lambda 表达式的形参类型,这是因为 lambda 表达式在调用时会根据传入的实参进行类型推导。

// 错误,普通函数形参不能使用 auto 且无初始化
// void func(auto param) {} 

// C++14 及以后支持,lambda 表达式可使用 auto 形参
auto lambda = [](auto x) { return x * 2; };

2.不能用于类的非静态成员初始化,没有对象无法推导

非静态成员变量:类的非静态成员变量是与类的对象相关联的,每个对象都有自己独立的一份非静态成员变量。在类定义时,并没有实际的对象存在,所以编译器无法根据某个初始化表达式来推导 auto 的具体类型。

静态非常量成员变量:静态成员变量属于类本身,而不是某个具体的对象。类的静态非常量成员变量不允许在类内部直接初始化,需要在类外部进行定义和初始化,所以使用 auto 在这里也不合适,因为编译器无法在类定义阶段完成类型推导。

静态常量成员变量静态常量成员变量可以在类内部直接初始化,并且使用 auto 时,如果初始化表达式是常量表达式,编译器可以在编译阶段完成类型推导,所以 static const auto 是可行的。

class T {
public:
    auto v1 = 0; // 错误,非静态成员不能用 auto 初始化
    static auto v2 = 0; // 错误,静态非常量成员不能在类内用 auto 初始化
    static const auto v3 = 0; // 正确,静态常量成员可以用 auto 初始化
};

// 静态非常量成员需要在类外定义和初始化
// 如果 v2 定义为 static int v2; ,则需要在类外进行如下定义
// int T::v2 = 0; 

3.不能使用 auto 关键字定义数组

使用数组初始化 auto 变量:当使用数组名初始化 auto 变量时,数组会退化为指针,所以 auto 会被推导为指针类型。例如 auto t1 = array; 中,t1 被推导为 int* 类型。

使用 auto 定义数组:使用 auto 直接定义数组是不允许的,因为编译器无法根据数组的初始化列表准确地推导出数组的类型和大小。

例如 auto t2[] = array; 和 auto t3[] = {1, 2, 3, 4, 5}; 都是错误的。

int array[] = {1, 2, 3, 4, 5};
auto t1 = array; // 正确,t1 被推导为 int* 类型
// auto t2[] = array; // 错误
// auto t3[] = {1, 2, 3, 4, 5}; // 错误

4.无法使用 auto 推导出模板参数

模板参数在编译时需要明确的类型信息,而 auto 是在变量声明时根据初始化表达式进行类型推导的,它无法直接为模板参数提供明确的类型。模板实例化过程中,编译器需要确切知道模板参数的类型才能生成相应的代码。

template<typename T>
struct Test{
}
int func()
{
    Test<double> t; //m模板实例化
    Test<auto> t1 = t;//错误,无法推导出模板类型
    return 0;
}

四.补充:decltype

1.推导规则:

1.+表达式为(1)普通变量或者(2)普通表达式或者(3)类表达式,在这种情况下,使用decltype推导出的类型和表达式的类型是一致的。

2.表达式时函数调用,使用decltype推导出的类型和函数返回类型一致。

纯右值:返回的时候要忽略const等限定符。

3.表达式是一个左值(可以取地址的变量),或者被括号包围,使用decltype推导出的是表达式类型的引用

如果有(const、volatile限定符不能忽略)

#include <iostream>
using namespace std;
class Test
{
public:
    const int num = 9;
    string tex;
};

int main()
{
    int a=10;
    decltype(a) b = 99;
    decltype(a+3.14) c = 52.13;
    decltype(a+b*c) d = 520.1314;
    decltype(Test::num) e = 0;

    return 0;
}

2.典型应用

泛型编程中的应用:

#include <iostream>
#include <list>

using namespace std;

template<class T>
class Container
{
public:
    void print(T& t)
    {
        for(m_it=t.begin();m_it != t.end();++m_it)
        {
            cout << "value:" << *m_it << endl;
        }
    }
private:
    //T() 表示调用类型 T 的默认构造函数来创建一个 T 类型的临时对象
    decltype(T().begin()) m_it; //自动类型推导容器的迭代器类型
};

int main()
{
    list<int> ls{1,2,3,4,5};
    Container<list<int>> c;
    Container<const list<int>> c2;
    c2.print(ls);
    return 0;
}

返回值类型后置:

语法:

auto func(参数1,参数2,...) ->decltype(参数表达式)

使用:

#include <iostream>
using namespace std;
//后置
//语法:auto func(参数1,参数2,...) ->decltype(参数表达式)
template<class T,class U>
auto add(T t, U u) ->decltype(t+u)
{
    return t+u;
}

int main()
{
    int x = 520;
    int y = 13.14;

    auto ret1 = add<int , double>(x,y);
    auto ret2 = add(x,y);//自动类型推导
    cout << "ret1:" << ret1 << endl;
    cout << "ret2:" << ret2 << endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值