引用

c++ 11:

  • auto->decltype

前言

auto是一种自动推导的类型,之前学过但是用的比较少。
主要也是c++不像java吧,java这种后端项目好像即使大版本更新了,项目也不会主动去换jdk。
c++稍微自由一点,听过的感觉都逐步靠近新版本,但肯定不是绝对,毕竟旧项目不考虑升级单纯维护的话也用不到那么新的。

auto好像还是c++11有的,c++14之后对auto又提供了什么也还不知道。

既然可以自动推导,那么可以用来定义变量或者函数

1
2
3
4
5
6
7
8
9
10
11
auto a = 123; //等价与 int a = 123; auto定义的时候必须有初值,否则无法确定类型,无法确定类型也就无法确定大小。

auto ave(int a,int b){
return a + b;
}
/*
等价于
int ave(int a, int b){
return a + b;
}
*/

但是这两种使用场景并不是auto最恰当的用法,不管是函数或者变量,其实不推荐auto去推导来声明


正文

自动推导的基础就是有关键信息,当信息不够的时候auto也无能为力。
同时auto肯定有缺点:

  1. auto不能保留const属性
    1
    2
    3
    const int a{};
    auto c = a;
    //c只会是int,而非const
  2. auto优先推导为值类型,而不是引用
    1
    2
    3
    4
    int a = 5;
    const int &b = a;
    auto c = a;
    //c为int类型,而不是const&
  3. auto利用返回值确定类型的时候会执行函数。
    1
    2
    auto x = ave(1,3);
    //类型将由函数返回值而定

其实默认情况下还是需要自己推导的,不过最新的intellisense自动推导类型了
源代码应该是这样的没啥特殊:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>

auto toMax(int &a, int &b){
return a > b ? a : b;
}

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

int main(){

return 0;
}

intellisense自动推导后:
有好处也有坏处,好处自然省了推导,坏处就是自己不能推导了,虽然有点废话文学但是无关紧要你可以关掉。

当然前面也说了auto的缺点,所以我们指定类型:

1
2
3
int& toMax(int &a, int &b){
return a > b ? a : b;
}

传递是引用,返回也是引用,那么返回的引用就是谁大的结果。

实际调用的时候还能玩一收骚操作:toMax(a, b) = 500;

1
2
3
4
5
int a = 150;
int b = 200;
toMax(a, b) = 500;

std::cout << a << " " << b << "\n";

找个样例测试一下:
有意思,发现b的值果然被修改了。

再回头想一想auto是否能这么做?

1
2
3
4
5
6
7
8
auto toMax(int &a, int &b){
return a > b ? a : b;
}
int main(){
int a = 150;
int b = 200;
toMax(a, b) = 500;
}

显然是不可以的,因为之前就说过,auto优先推断类型为值类型传递。
也就是先前截的图,auto的时候intellisense自动推导了类型是int类型的返回值,既然是int类型那么修改肯定就不合法了,因为返回的是一个值,而不是一个变量。

当然这样一搞,显得auto很呆,所以后面的标准又加入了新的讨论:拖尾函数

1
2
3
auto toMax(int &a, int &b)->int&{
return a > b ? a : b;
}

能看到编译器在形式上通过了这种写法。不过具体用处在这还体现不出来要等以后再看。


decltype

这玩意怎么说呢,通过一个表达式去获得类型,比如说:

1
2
3
int a{};
float b{};
decltype(a-b) c; //c就向float靠齐了

如果表达式只是一个变量,那么推断类型就为该变量的类型。
减法是要看隐式转换的条件,或者说等级。
或许这样粗看感觉还不如auto

但是,decltype可以保留const和引用类型!

1
2
3
4
5
6
7
8
9
int a{};
const int b{};
int &la {a};
int *p {&a};

//decltype(a) x; -> 相当于 int x
//decltype(b) x; -> 相当于 const int x
//decltype(la) x; -> 相当于 int &x
//decltype(p) x; -> 相当于 int *x

这简直就是哦买噶~

但是还有一种经历运算之后的操作:

1
2
3
4
5
6
7
8
9
10
11
12
int a = 100;
int b = 200;
int *p = &a;

//原则上是根据运算结果是否有固定的内存地址,也就是左值。
//如果有固定的内存地址类如指针 推导的类型就为该类型的引用类型
//如果没有固定的内存地址,则推导的类型就为该结果的类型

decltype(a+b); // -> 相当于 int x
decltype(*p); // -> 相当于 int &x,*号其实也是一种间接的运算符
decltype(p[0]); // -> 相当于 int &x

这样说可能有点绕,自己多敲几个看看得到的是什么类型的变量

然后还有一种比较简单的就是根据函数返回值来确定:

1
2
3
4
int ave(int a,int b){
return a + b;
}
decltype(ave(100,200)) x; //->显然是int类型

但是注意decltype不会执行函数,只会通过数据类型去猜,而auto是会去执行函数的


auto->decltype

已经知道decltype套函数的时候不会调用,而且decltype可以推导const和引用等类型

1
2
3
auto toMax(int &a, int &b)->int&{
return a > b ? a : b;
}

像这种直白的写法可以说拜拜!

1
2
3
auto toMax(int &a, int &b)->decltype(a > b ? a : b){
return a > b ? a : b;
}

根据这样的推导是求出了引用的类型,但是写法上好似有重复,又有点累赘。

所以在c++ 14以后引入了新的写法,上述的写法是基于c++11

1
2
3
decltype(auto) toMax(int &a, int &b){
return a > b ? a : b;
}

这种写法就可以不使用拖尾函数了,但是编译器要支持c++14所以用这种写法要注意。


结语

自动推导感觉。。还是慎用吧,除非你很严谨,不然到时候找翻天了。