decltype
✍ dations ◷ 2024-12-23 05:36:49 #C++
在C++程序设计语言中,decltype作为操作符(英语:Operator (programming)),用于获取表达式的数据类型。C++11标准引入decltype,主要是为泛型编程而设计,以解决泛型编程中有些类型由模板参数决定而难以(甚至不可能)表示的问题。
从语义上说,decltype的设计适合于通用库编写者或编程新手。总体上说,对于变量或函数参数作为表达式,由decltype推导出的类型与源码中的定义可精确匹配。而正如sizeof(英语:sizeof)操作符一样,decltype不对操作数求值。
虽然C++规范或大多数的文档及书籍都将decltype定义为一个操作符,但是针对初学者而言,应将此操作符理解为一种特殊的类型声明,而不是像一些强类型语言那样将此操作符理解为一个带返回值的函数。为说明此问题,举例如下:
对于其它语言的开发者,可能写出如下的代码:
#include <iostream>using std::cout;int main(){ int n = 12; cout << decltype(n);//实际上decltype完全不同于js或C#的typeof getchar(); return 0;}
相反,可以将decltype视为一种特殊的类型声明方式,C++语言里面类似的功能为自动变量。
举例:设开发人员无法确定一个表达式exp()的值类型,然而需要声明一个与之类型匹配的变量,则可以如下声明:
decltype(exp()) x;
随着C++引入模板,以及由标准模板库引领的泛型编程逐渐兴起,实现一个能获取表达式类型的机制的需求便由此出现,而这一机制常称为typeof。在泛型编程中,若类型由函数参数决定,则获知之常非易事,在需要获取函数模板实例化的返回类型时尤然。
为此,许多编译器厂商都基于程序语言现有的功能,自行实现了这类操作符,其实现如typeof(英语:typeof),以及一些功能有限,但更易移植的实现,以满足这一需求。早在C++还未完全标准化的1997年,布莱恩·帕克(Brian Parker)就基于sizeof操作符,提出了一种可移植的解决方案。对此,比尔·吉本斯(Bill Gibbons)则提出,这一方案仍有诸多限制,而且通常来说,直接引入typeof机制效果都更好。2000年10月,安德烈·亚历山德雷斯库在IT技术杂志《Dr. Dobb's Journal(英语:Dr. Dobb's Journal)》上评论道:“(若)有typeof(操作符),撰写和理解模板代码就会便易许多。”他也提到“typeof和sizeof(操作符)有相同的后端,(这是)因为sizeof无论如何必须去计算类型。”安德鲁·克尼格(英语:Andrew Koenig (programmer))与芭芭拉·E·摩(Barbara E. Moo)也谈到内建于程序语言中的typeof功能非常有用,但也提醒道“使用时常会引入一些难以发觉的程序错误,且尚有无法解决的问题(即并非万用)。”并提出可以利用类型转换(如使用标准模板库所提供的typedef),更有效、更通用地实现这一功能。但是,史蒂夫·丹斯特(Steve Dewhurst)则称如此转换“在设计与发布上花费巨大”,而且“采用直接提取表达式类型的方法更简单。”(大意)2011年间,在一片关于C++0x的文章中,克尼格和摩预言道:“decltype将会广泛用于为每日的程序编写提供便利。”
2002年间, 比雅尼·斯特劳斯特鲁普提议扩充C++程序语言,为之引入查询表达式类型,以及不必指明类型便可初始化对象的机制。斯特劳斯特鲁普注意到,在GCC与EDG(英语:Edison Design Group)编译器中,typeof所提供的“引用丢弃”(reference-dropping)语义可能存在问题;另一方面,若使用基于表达式左值性、返回一个引用类型的操作符实现之,又难以理解。于是,在呈交给C++标准委员会的初始提案中,便将两种实现方法杂糅起来:只有当表达式的声明类型包含一个引用时,操作符才会返回一个引用类型。为强调推导出的类型能确实反映表达式的声明类型,提案中提议将此操作符命名为decltype。提案还提及了decltype的一项主要设计初衷,也即让编写完美的转发函数成为可能。在编程时,程序员有时需要编写一个泛型转发函数,使之不论以何种类型实例化,都能返回同于包装函数的类型,而若无decltype操作符,就几乎不可能做到这一点。decltype的样例代码如下所示,其中利用了C++11标准中的“返回类型后置”(trailing-return-type)语法。
int& foo(int& i);float foo(float& f);template <class T> auto transparent_forwarder(T& t) −> decltype(foo(t)) { return foo(t);}
decltype便是本段代码的核心部分,用于保存“包装函数是否返回一个引用类型”这一信息 。
类似于sizeof操作符,decltype不对其操作数求值。粗略来说,decltype(e)返回类型前,进行了如下推导:
这些语义是为满足通用库编写者的需求而设计,但由于decltype的返回类型总与对象(或函数)的定义类型相匹配,这对编程新手来说也更为直观。更正式地说,规则1适用于不带括号的标识符表达式(id-expression)与类成员访问表达式。示例如下:
const int&& foo();const int bar();int i;struct A { double x; };const A* a = new A();decltype(foo()) x1; // 类型为const int&&decltype(bar()) x2; // 类型为intdecltype(i) x3; // 类型为intdecltype(a->x) x4; // 类型为doubledecltype((a->x)) x5; // 类型为const double&