C++ 中的 typeof、auto 以及 decltype

类型别名 typedef/using

类型别名 (type alias) 是一个名字,它是某种类型的同义词。使用类型别名有很多好处,它让复杂的类型名字变得简单明了,易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。
有两种方法可以定义类型别名,传统的方式是使用关键字 typedef
示例

typedef double wages;
typedef wages base, *p;
...
base pi = 3.14;		// pi 是 double 类型
p ptr = π		// ptr 是 double* 类型

除了 typedef 以外,C++ 11 还有一种新的方法,使用 别名声明 (alias declaration) 来定义类型的别名:
using str = std::string;
这种使用 using 作为别名声明的开始,其后紧跟别名和等号的作用就是把等号左侧的名字规定成等号右侧类型的别名。
指针、常量和类型别名
如果某个类型别名指代的是复合类型或常量,那么把它用到声明语句里可能会产生意想不到的后果。

typedef char* pstring;
const pstring cstr = 0;		//cstr 是一个指向 const char 对象的指针
const pstring* pps;		//pps 是一个指针,指向一个 const pstring 对象的指针

值得注意的是, pps 与 const char** 是有所区别的,假设有一个 const char** ppc
那么

*(*pps) = 'c';	// ok
*(*ppc) = 'c';	// compile error

这是因为 const pstring* 中的 const 只限制到了 pstring 这一层
const char** 是底层 const ,它使得所有的解引用出来的对象都变成 const 对象

类型说明符 auto

C++ 11 引入 auto 类型说明符,用它就能让编译器替我们去分析表达式所属的类型。 auto 让编译器通过初始值来推算变量的类型。显然,auto 定义的变量必须有初始值。
使用示例

auto i = 10;		// i 是 int 类型
auto pi = 3.14;		// pi 是 double 类型

使用 auto 也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型都必须一样:

auto i = 0, * p = &i;		// ok
auto sz = 10, pi = 3.14;	// compile error, 类型不一样会引起编译错误

复合类型、常量 auto
使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为 auto 的类型
示例

int i = 10, & r = i;
auto a = r;		// a 是 int 类型

其次,auto 一般会忽略顶层 const 但是不会忽略底层的 const
示例

const int ci = i, & cr = ci;
auto b = ci;	// b 是 int
auto c = cr;	// c 是 int
auto d = &i;	// d 是 int* 指针
auto e = &ci;	// a 是 const int* 指针

如果需要让 auto 是顶层 const ,那么需要在 auto 前面加 const 修饰
const auto f = ci;

还可以将引用的类型设置为 auto ,此时原来的初始化规则仍然适用:

auto &g = ci;		// g 是 const int&
auto &h = 42;		// compile error 不能对字面值设置普通的引用
const auto &j = 42;	// j 是 const int& 引用到了一个临时量

设置一个类型为 auto 的引用时,初始值中的顶层常量属性仍然保留。和往常一样,如果我们给初始值绑定一个引用,则此时的常量就不是顶层常量了。 auto 会自己视情况增加 const 修饰

auto k = ci, &l = i;	// k 是 int, l 是 int&
auto &m = ci, *p = &ci;	// m 是 const int& , p 是 const int*
auto &n = i, *p2 = &ci;	// compile error, i 是 int, ci 是 const int

类型指示符 decltype

C++ 11 引入 类型指示符 decltype 它的作用是选择并返回操作数的数据类型,编辑器会分析表达式的类型,却不去计算表达式的值:
decltype (func()) sum = x;
编译器不去计算 func() 而是使用返回值的类型作为 sum 的类型。
示例

const int ci = 0, &cj = ci;
decltype (ci) x = 0;			// x 是 const int
decltype (cj) y = x;			// y 是 const int&
decltype (cj) z;			// compile eror, z 是 const int& 但是没有初始化

值得注意的一点是,假设有 decltype(cj) z; 当 cj 是一个引用时, z 也会是一个引用。这在整个 C++ 中都是一个特殊现象,引用从来都是作为其所指对象的同义词或者别名出现,但是这里是作为引用出现。

decltype 和引用
如果 decltype 使用的表达式不是一个变量,那么 decltype 会返回表达式结果对应的类型。如果有的表达式返回一个引用,那么 decltype 声明语句的类型就是引用:

int i = 42, * p = &i, &r = i;
decltype (r + 0) b;		// r + 0 返回一个 int 类型,因此 b 是 int 类型
decltype (*p) c;		// compile error, decltype 认为 *p 是 int&

因为 r 是一个引用,因此 decltype (r) 的结果就是一个引用类型。如果想让结果的类型是 r 引用的对象的类型,那么可以把 r 作为表达式的一部分,例如 (r + 0) 这样这个表达式的类型就是一个具体值,而不是引用了。
值得注意的点是,如果表达式的内容是解引用操作,那么 decltype 会得到一个引用类型。解引用指针可以得到指针所指的对象,而且还能对这个对象赋值。因此decltype (*p)的结果类型就是 int& 而不是 int。
decltype 和 auto 的另外一个充要区别是,decltype 的结果类型与表达式形式密切相关。有一种情况需要特别注意:
对于 decltype 所用的表达式来说,如果变量名加上了一对括号,则得到的类型与不加括号时有所不同。如果 decltype 使用的是一个不加括号的变量,则得到的结果就是该变量的类型:如果给变量加上了一层或者多层括号,便会把它当做一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,对于这样的表达式 decltype 会得出引用类型。
示例

decltype ((i)) d;	// d 是 int& 类型
decltype (i) e;		// e 是 int 类型

切记:如果 decltype ((variable)) 双层括号的结果永远是引用,而 decltype(variable) 的结果只有 variable 是引用时才是引用。

赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int 类型,则表达式
i = x 的类型是 int& 。
示例

int a = 3, b = 4;
decltype (a) c = a;			// c 是 int 类型
decltype (a = b) d = a;			// d 是 int& 类型
posted @ 2023-01-13 19:12  文工程序  阅读(1030)  评论(0编辑  收藏  举报