auto占位符(C++11~C++20)

重新定义的auto关键字(C++11)

        auto是C语言的一个旧关键字,和static一样用来声明某变量为局部的(local)。但它从未被正在用过,因为"不指明某物为static"也就隐含地声明了它是个auto。

        于是在C++11中赋予了auto新的含义:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。例如:

1)................................................................................
/*当使用条件表达式初始化auto声明的变量时,编译器总是使用表达能力更强的类型,因此下面的i会被
推导为double类型*/
auto i = true ? 5 : 8.0;

2)................................................................................
/*如果auto声明的变量是按值初始化,则推导出的类型会忽略cv限定符(const和volatile)。进一步解
释为,在使用auto声明变量时,既没有使用引用也没有使用指针,那么编译器在推导的时候会忽略cv限
定符。当然auto本身也支持添加cv限定符:*/
const int i = 5;
auto j = i;    //按值初始化,auto类型推导为int,而非const int
auto &m = i;    //按引用初始化,auto类型推导为const int, m推导类型为const int&
auto *m = &i;    //按指针初始化,auto类型推导为const int, m推导类型为const int*
const auto n = j;    //const和auto可以结合使用,auto推导类型为为int, n的类型为const int

3)................................................................................
class Base {
public:
    virtual void fun() {std::cout << "Base::fun" << std::endl;};
};
class Derived : public Base {
public:
    virtual void fun() override {std::cout << "Deriverd::fun" << std::endl;};
};

Base* d = new Derived();
/*下面的语句是按值赋值,因此auto直接推导为Base。代码自然会调用Base的复制构造函数,也就是
说Derived被切割成了Base, 这里的b.fun()最终调用Base的fun函数*/
auto b = *d;
b.fun();

4)................................................................................
/*使用auto声明变量初始化时,目标对象如果是引用,则引用属性会被忽略:*/
int i = 5
int &j = i;
auto m = j;    //auto推导类型为int, 而非int&

5)................................................................................
/*使用auto和万能引用声明变量时,对于左值会将auto推导为引用类型。当右值引用&&与类型推导
结合在一起时,将变得非常灵活。它既可以与左值绑定也可以与右值绑定,此时它变成一种通用引用类
型(universal reference)*/
int i = 5;
auto&& m = i;    //auto推导类型为int&
auto&& j = 5;    //auto推导为int

6)................................................................................
/*需要注意的是,如果使用auto声明变量的,则会导致其他程序员阅读代码时需要翻阅初始化变量的具体
类型,那么我们就需要慎重考虑是否适合使用auto关键字。不过在类型比较复杂的场景,比如在容器的迭
代器上、lambda表达式、bind等直接使用auto。*/
/*示例1*/
std::map<std::string, int> str2int;
//...填充str2int的代码
/*下面的auto被推导为std::map<std::string, int>::const_iterator*/
for(auto it = str2int.cbegin(); it != string2int.cend(); ++it) {}
//或者
/*下面的auto被推导为std::pair<const std::string, int>*/
for(auto &it : str2int) {}
/*示例2*/
/*这里的1可能是xxx::<lambda_xxxxxxxxxxxx>,不同的编译器命名方法可能不同,我们根本无法写出
其类型,只能用auto来声明*/
auto 1 = [](int a1, int a2) {return a1 + a2}
/*示例3*/
int sum(int a1, int a2) {return a1 + a2}
/*std::_Binder<std::_Unforced, int(__cdec1 &)(int, int), int, const std::_Ph<1> &>便
是b推导的类型,太复杂了,所以直接使用auto*/
auto b = std::bind(sum, 5, std::placeholders::_1);

7)................................................................................
/*fun函数中的auto是一个返回值占位符,真正的返回类型是函数指针类型int(*) (int)。这里func函
数声明采用了C++11引入的函数返回类型后置的方法*/
int impl(int x)
{
    return x;
}

auto func()->int(*)(int)    //返回后置类型,auto为返回值占位符
{
    return impl;
}

返回类型推导 (C++14)

        C++14标准支持对返回类型声明为auto的推导,例如:

/*下面代码,编译器会推导出auto为int。需要注意的是如果函数内有多个返回值,那么需要保证每次的
返回值类型都是相同的,否则会编译不过*/
auto sum(int a1, int a2) {return a1 + a2;}

lambada表达式形参 (C++14)

         在C++14中,我们还可以把auto写到lambda表达式的形参列表中,这样就得到了一个泛型的lambda表达式,例如:

/*下面代码中a1被推导为int类型,a2被推导为double类型,返回值reval被推导为double类型*/
auto 1 = [](auto a1, auto a2) {return a1 + a2;};
auto reval = 1(5, 5.0);

/*下面是lambda表达式返回auto引用的方法。起初在后置返回类型中使用auto是不允许的,但是后
来人们发现,这是唯一让lambda表达式通过推导返回引用类型的方法了*/
auto 1 = [](int &i)->auto& {return i;}
auto x1 = 5;
auto &x2 = 1(x1);
assert(&x1 == &x2);    //有相同的内存地址

声明成员变量时的初始化 (C++11 ~ C++17)

        虽然C++11已经支持在声明成员变量时初始化,但是auto却无法在这种情况下声明非静态成员变量;另外静态成员变量是可以用auto声明并且初始化的,不过前提是auto必须使用const限定符,遗憾的是const限定符会导致i常量化,显然这不是我们想要的结果。

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

struct sometype {
    static const auto i = 5;
};

        幸运的是,在C++17标准中,对静态成员变量,auto可以在没有const的情况下使用,例如:

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

列表初始化对象的类型推导的新规则(C++17)

        当auto关键字与列表初始化组合时,这里的规则有新老二个版本,这里只介绍C++17引入的新规则:

        a. 直接使用列表初始化,列表中必须为单元素,否则无法编译,auto类型被推导为单元素的类型。

        b.用等号加列表初始化,列表中可以包含单个或者多个元素,auto类型被推导为std::initializer_list<T>, 其中T是元素类型。请注意,在列表中包含多个元素的时候,元素类型必须相同,否则编译会报错。

auto x1{1, 2};    //编译失败,不是单元素
auto x2{1};    //x2类型为int
auto x3 = {1, 2};    //x3类型为std::initializer_list<int>
auto x4 = {1};    //x4类型为std::initializer_list<int>
auto x5 {1, 2.0};    //编译失败,花括号中的元素类型不同

非类型模板形参占位符 (C++17)

        C++17中,对auto关键字又一次进行了扩展,使它可以作为非类型模板形参的占位符。当然,我们必须保证推导出来的类型是可以用作模板形参的,否则无法通过编译。

#include <iostream>

templete<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声明形参(注意在C++14中,auto可以为lambda表达式声明形参)

void fun(atuo str) {...}    //C++20之前编译失败,C++20编译成功

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张帅峰_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值