C++中auto、decltype的用法

auto占位符

学过C语言的同学都知道auto是用来声明变量为自动类型的,尽管如此,auto很少被C工程师使用。因此在C++11标准赋予了auto新的含义:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符

auto使用限制

在使用auto占位符声明变量的时候必须初始化变量。例如以下例子就会报错

auto i;
i = 5; // 编译失败

auto的推导规则

1.当用一个auto关键字声明多个变量的时候,编译器遵从由左往右的推导规则,以最左边的表达式推断auto的具体类型

int n = 5;
auto *pn = &n, m = 10;
//auto *pn = &n, m = 10.0;// 编译失败,声明类型不统一

2.当使用条件表达式初始化auto声明的变量时,编译器总是使用表达能力更强的类型

auto i = true ? 5 : 8.0;// i的数据类型为double

 3.如果auto声明的变量是按值初始化,且没有使用引用,也没有使用指针,则推导出的类型会忽略cv限定符

const int o = 5;
auto p = o; // auto推导类型为int,而非const int
auto &q = o; // auto推导类型为const int,q推导类型为const int&
auto *r = &o; // auto推导类型为const int,r推导类型为const int*
const auto s = p; // auto推导类型为int,s的类型为const int

4.使用auto声明变量初始化时,目标对象如果是引用,则引用属性会被忽略

int t = 5;
int &u = t;
auto v = u; // auto推导类型为int,而非int&

 5.使用auto和万能引用声明变量时,对于左值会将auto推导为引用类型

int w = 5;
auto&& x = w; // auto推导类型为int& (这里涉及引用折叠的概念)
auto&& y = 5; // auto推导类型为int

6.使用auto声明变量,如果目标对象是一个数组或者函数,则auto会被推导为对应的指针类型

int z[5];
auto a = z; // auto推导类型为int*
auto b = sum; // auto推导类型为int(*)(int,int)

auto在不同C++标准中的差异

C++11标准

支持在声明静态成员变量时使用auto声明并初始化,但前提是auto必须使用const限定符

struct sometype {
    auto i = 5;
};// 错误,无法编译通过


struct sometype {
    static const auto i = 5; //C++11
}; //正确

C++14标准

在C++14标准支持对返回类型声明为auto的推导

auto sum(int a1, int a2) { return a1 + a2; }

在C++14中,auto可以为lambda表达式声明形参

auto l = [](auto a1, auto a2) { return a1 + a2; };
auto retval = l(5, 5.0);

C++17标准

在C++11标准中,auto修饰静态成员变量时必须使用const修饰,这会导致成员变量常量化,为了解决这个问题,在C++17标准中,对于静态成员变量,auto可以在没有const的情况下使用

struct sometype {
    static auto i = 5; // C++17
};

C++17标准对auto关键字又一次进行了扩展,使它可以作为非类型模板形参的占位符

#include <iostream>
template<auto N>
void f()
{
    std::cout << N << std::endl;
}
int main()
{
    f<5>();  // N为int类型
    f<'c'>();// N为char类型
    f<5.0>();// 编译失败,模板参数不能为double
}

C++20标准

C++20之前的标准,无法在函数形参列表中使用auto声明形参

void echo(auto str) {…} // C++20之前编译失败,C++20编译成功

decltype说明符

使用限制

上一章节,我们介绍了auto,得知auto不可以在非静态成员变量上使用,在C++20之前不可以在函数形参列表中使用,但是上述这些限制对于decltype说明符都不存在

struct S1 {
    int x1;
    decltype(x1) x2;
    double x3;
    decltype(x2 + x3) x4;
};

int x1 = 0;
decltype(x1) sum(decltype(x1) a1, decltype(a1) a2)
{
    return a1 + a2;
}
auto x2 = sum(5, 10);

看到这里,我们感觉decltype好像没有什么用,但事实上decltype主要的用处在函数返回类型后置,库函数的编写者可能使用的更多。

decltype的推导规则

1.如果e是一个未加括号的标识符表达式(结构化绑定除外)或者未加括号的类成员访问,则decltype(e)推断出的类型是e的类型T。如果并不存在这样的类型,或者e是一组重载函数,则无法进行推导。
2.如果e是一个函数调用或者仿函数调用,那么decltype(e)推断出的类型是其返回值的类型
3.如果e是一个类型为T的左值,则decltype(e)是T&
4.如果e是一个类型为T的将亡值,则decltype(e)是T&&
5.除去以上情况,则decltype(e)是T

6. 通常情况下,decltype(e)所推导的类型会同步e的cv限定符,但是还有其他情况,当e是未加括号的成员变量时,父对象表达式的cv限定符会被忽略,不能同步到推导结果。

    int i;
    int *j;
    int n[10];
    decltype(i=0) a = i; //规则3
    decltype(0,i) b = i; //规则3
    decltype(i,0) c; //规则5
    decltype(n[5]) d = i; //规则3
    decltype(*j) e= i; //规则5
//    decltype(static_cast<int&&>(i)) iii; //规则4
    decltype(i++) g; //规则5
    decltype(++i) h= i; //规则3


    const int ia = 0;
    decltype(ia); // 规则6 decltype(i)推导类型为const int
    struct A {
        double x;
    };
    const A* aa = new A();
    decltype(aa->x);  //规则6 decltype(a->x)推导类型为double, const属性被忽略
    decltype((aa->x));//规则3+6 decltype((aa->x))推导类型为const double&

使用场景

C++11中函数返回类型后置

在C++11标准中auto作为占位符并不能使编译器对函数返回类型进行推导,所以就有了以下用法

auto sum(int a1, int a2)->int
{
    return a1+a2;
}


template<class T1, class T2>
auto sum(T1 a1, T2 a2)->decltype(a1 + a2)
{
    return a1 + a2;
}
auto x4 = sum(5, 10.5);

需要推导函数返回值为引用类型的情况

C++14中auto已经可以对函数返回类型进行推导,那么decltype是不是就没有存在的意义了,答案当然是否定的了,例如以下代码

template<class T>
auto return_ref(T& t)
{
    return t;
}

int x1 = 0;
static_assert(
    std::is_reference_v<decltype(return_ref(x1))>
);// 编译错误,返回值不为引用类型

根据auto推导规则4,该函数返回的类型是T类型,假如我们实际需要它返回T&类型,这个时候就需要我们的decltype上场了(规则1)

template<class T>
auto return_ref(T& t)->decltype(t)
{
    return t;
}

int x1 = 0;
static_assert(
    std::is_reference_v<decltype(return_ref(x1))>
);

decltype(auto)

在C++14标准中出现了decltype和auto两个关键字的结合体:decltype(auto)。

它的作用简单来说,就是告诉编译器用decltype的推导表达式规则来推导auto。另外需要注意的是,decltype(auto)必须单独声明,也就是它不能结合指针、引用以及cv限定符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rookie Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值