您现在的位置是:首页 >技术教程 >盘一盘C++的类型描述符(五)网站首页技术教程
盘一盘C++的类型描述符(五)
先序文章请看
盘一盘C++的类型描述符(四)
盘一盘C++的类型描述符(三)
盘一盘C++的类型描述符(二)
盘一盘C++的类型描述符(一)
类型推导
decltype
其实auto
跟decltype
完全就是两个世界的人,二者表达的语义完全不同。但因为它们都有「类型推导」的作用,因此总是有人会将它们混淆,它们也总是能被捆绑式提及。
前面我们q强调过,auto
其实是一个占位符,我们可以把它理解成方程中的未知数,编译期会找出这个方程的最简解,然后替换auto
,从而成为一个完整的表达式。
而decltype
则是一个编译期的运算符,用于求一个表达式的类型的,它就是非常简单粗暴地返回这个表达式的类型罢了,并没有过多的复杂规则。我们来看几个例子:
int f1();
int &f2();
const int f3();
const int &f4();
int &&f5();
void Demo() {
int a = 5;
int &r1 = a;
const int &r2 = a;
int &&r3 = std::move(a);
using type1 = decltype(a); // int
using type2 = decltype(&a); // int *
using type3 = decltype(std::move(a)); // int &&
using type4 = decltype(r1); // int &
using type5 = decltype(r2); // const int &
using type6 = decltype(r3); // int &&
using type7 = decltype(f1); // int (), 函数类型
using type8 = decltype(&f1); // int (*)(), 函数指针类型
using type9 = decltype(f1()); // int, 因为有调用,所以这里取的是返回值类型
using type10 = decltype(f2()); // int &
using type11 = decltype(f3()); // int, 这里需要额外解释一下
using type12 = decltype(f4()); // const int &
using type13 = decltype(f5()); // int &&
int arr[] {1, 2, 3};
using type14 = decltype(arr); // int [3]
using type15 = decltype(&arr); // int (*)[3]
using type16 = decltype(arr[0]); // int &,注意下角标运算是引用传递
using type17 = decltype("abc"); // const char(&) [4]
}
大多数的相信读者都没有问题,看例子就能明白。这里我需要额外解释三个,type7
、type11
和type17
。
首先我们来看一下type7
,这是对一个函数本身进行decltype
,所以得到的也应该是「函数」类型,所以我们要注意,如果你尝试拿函数类型来「定义变量」的话,得到的并不是变量,而是函数声明,比如说:
int f();
void Demo() {
using type = decltype(f); // int ()类型
type f2; // 相当于int f2(),所以这是一个函数声明
// 如果要定义函数指针类型的变量,则需要使用对应的指针类型
// 方法1:函数取地址后,推导出函数指针类型
using type2 = decltype(&f); // int (*)()类型
type2 pf; // 相当于int (*pf)(),所以这是定义了一个变量
// 方法2:给函数类型加上指针(使用type_traits),变为对应的函数指针类型
std::add_pointer_t<type> pf2; // 相当于int (*pf2)()
}
这一点希望读者明晰,因为decltype
只会取表达式本身的类型,而不会进行任何额外处理,所以自然,对函数来说取的也是函数本身的类型。对数组类型也是同样的,会取出数组类型,而不是指针类型(就如上面的type14
)。
我们再看看一下type11
,f3
的函数原型是const int ()
,照理说返回值应该是const int
,但为什么这里却推出了int
呢?原因在于,我们看到这里函数返回没有引用符,所以一定是「值传递」,因此这就相当于对一个右值进行decltype
。就像我们decltype(3)
出来的也是int
而不是const int
一样,右值本来就不存在只读不只读的问题,它一定要由一个对象来承接,否则无意义(纯右值就会直接丢弃,将亡值就会立即析构),因此,函数返回值中的const
是无意义的! 当然这里说的是类型本身的const
,不是解类型、原身类型中的const
。
再看下面几个例子,我们来体会一下,函数返回值中无用的const
:
const int f1();
const int *f2();
int *const f3();
const int &f4();
void Demo() {
using type1 = decltype(f1()); // int
using type1 = decltype(f1()); // const int *
using type1 = decltype(f1()); // int *
using type1 = decltype(f1()); // const int &
}
再次强调,解类型、原身类型中的const
不是修饰类型本身的,它不会被丢掉。
最后我们来看一下一开始的例子中的type17
,篇幅隔得有点远,所以我再写一遍:
using type17 = decltype("abc"); // const char(&)[4]
首先,字符串字面量会被映射为不可变的字符数组,这个笔者已经提及多次了。那么加上字符串结尾隐藏的'