C++11:类型推导和追踪函数返回类型decltype

    参考文章:https://blogs.oracle.com/pcarlini/entry/c_11_tidbits_decltype_part

    decltype 是 GCC 实现的第一个 C++ 11 新特性。它实际上起源于一个相当古老的 GNU 扩展关键字—— __typeof__。这个非标准关键字也能够在 C 语言中使用,GNU Compiler Collection 的专业用户可能对它更熟悉一些。2008 年,GCC 4.3.x 就实现了这个特性,同时去除了 __typeof__ 的一些缺点。现在,decltype 和 __decltype 两个关键字在 GCC 中都适用;前者只能用在 C++ 11 模式下,后者可以同时应用于 C++ 11 和
    C++ 98 模式。__typeof__ 则已经停止使用。

    下面来看看 decltype 的基本使用。简单来说,decltype 关键字用于查询表达式的类型。不过,这只是其基本用法。当这个简单的表述同 C++ 11 的其它特性结合起来之后,一些意想不到的有趣用法就此产生。 decltype 的语法是

    decltype ( expression )

    这里的括号是必不可少的。根据前面的说法,decltype 的作用是“查询表达式的类型”,因此,上面语句的效果是,返回 expression 表达式的类型。注意,decltype 仅仅“查询”表达式的类型,并不会对表达式进行“求值”。 先看一个最基础的例子:

    const int&& foo(); int i;

    struct A { double x; }; const A* a = new A();

    decltype(foo())  x1;  // const int&&      (1) decltype(i)      x2;  // int              (2) decltype(a->x)   x3;  // double           (3) decltype((a->x)) x4;  // double&          (4)

    传统的 __typeof__ 有一个颇为诟病的地方,在于不能很好地处理引用类型。而 decltype 则没有这个问题。而 decltype 实际上更好地融入了 C++ 11 类型系统,来看一个比较复杂的例子:

    int    i; float  f; double d;

    typedef decltype(i + f)  type1;  // float typedef decltype(f + d)  type2;  // double typedef decltype(f < d)  type3;  // bool

    上面的例子清楚看出,decltype 能够很好地处理类型转换这里问题。

    或许你会对上面代码中的 (4) 心生疑问。为什么 decltype((a->x)) 会是 double&?这是由 decltype 的定义决定的。decltype 判别的规律还是比较复杂的: 对于 decltype( e ) 而言,其判别结果受以下条件的影响: 1.

    如果 e 是一个标识符或者类成员的访问表达式,则 decltype(e) 就是 e 所代表的实体的类型。如果没有这种类型或者 e 是一个重载函数集,那么程序是错误的(上例中的 (2) 和 (3)); 2. 如果 e 是一个函数调用或者一个重载操作符调用(忽略 e 外面的括号),那么 decltype(e) 就是该函数的返回类型(上例中的 (1));

    3.

    如果 e 不属于以上所述的情况,则假设 e 的类型是 T:当 e 是一个左值时,decltype(e) 就是 T&;否则(e 是一个右值),decltype(e) 是T(上例中的 (4) 即属于这种情况。在这个例子中,e 实际是 (a->x),由于有这个括号,因此它不属于前面两种情况,所以应当以本条作为判别依据。而 (a->x) 是一个左值,因此会返回 double &)。

    说了这么多,decltype 到底有什么用?事实上,decltype 在复杂的模板编程中非常有用。不过,这需要结合我们前面提到的 auto 关键字。举个经典的例子,请看下面的代码:

    template

    ??? foo(T t, U u) {

    return t + u; }

    问号这里该填写什么呢?

    问题的关键在于,我们正在处理模板,因此我们根本不知道 T 和 U 的实际类型。即使这两个模板值实际都是 C++ 内置类型,我们也无法确切地知道它们的和的类型。在过去的 GNU C++ 运行时库中,我们可以使用前面说过的 __typeof__ 扩展,编写相当难看的代码:

    template

    decltype((*(T*)0) + (*(U*)0)) foo(T t, U u) {

    return t + u; }

    而在 C++11 中,我们可以使用 auto 关键字:

    template

    auto foo(T t, U u) -> decltype(t + u) {

    return t + u; }

    看起来好多了吧!现在这已经是语言级别的支持了。

    最后,我们来看一个更加实际的例子。在 GCC 的 C++11 运行时库中有这么一段代码:

    template<typename _IteratorL, typename _IteratorR>

    inline auto operator-(const reverse_iterator<_IteratorL>& __x,                       const reverse_iterator<_IteratorR>& __y)     -> decltype(__y.base() - __x.base()) {

    return __y.base() - __x.base(); }

    现在,这段代码应该更加清晰了。这实际上解决了 C++ 98 实现的一个真正的 bug。在 GCC 的 C++ 98 版本中,这段代码是这样的:

    template<typename _IteratorL, typename _IteratorR>

    inline typename reverse_iterator<_IteratorL>::difference_type operator-(           const reverse_iterator<_IteratorL>& __x,           const reverse_iterator<_IteratorR>& __y) {

    return __y.base() - __x.base(); }

    这段代码只有在这两方的 difference_type 相同时才适用。

时间: 2024-10-10 23:27:37

C++11:类型推导和追踪函数返回类型decltype的相关文章

c++标准14取消decltype推算函数返回类型

Table of Contents 1. c++11之前不支持auto关键字 2. c++11支持auto关键字 2.1. 但是不能自动推断函数返回类型 2.2. 使用-> decltype来声明返回类型 3. c++14让事情又回到简单 4. 我们该使用哪个c++版本 1 c++11之前不支持auto关键字 下面的代码在c++11中是不支持的 auto add(int a, int b) { int i = a + b; return i; } int main(int argc,char *

c/c++: c++函数返回类型什么情况带const

c++ 函数的返回类型,包括const 什么时候起作用呢? 函数返回值不想其立即修改的. 例子如下,这是一个简单的避免产生隐形返回变量的方法,abc 的函数返回是引用,main函数中第10行,++ 操作是基于 const int & 类型,所以会出错,但以后对改引用的操作不会受到const 约束. 这样的好处是避免了函数返回值与操作符的逻辑错误结合,例如下面的例子中函数返回的++,对于main 函数是不直观的,进一步的应用是在操作符重载方面,见下一情况说明. 1 const int & a

函数指针(函数指针作为函数形参/函数类型作为函数返回类型)

函数指针是指向函数的指针变量. 因此"函数指针"本身首先应是指针变量,只不过该指针变量指向函数.这正如用指针变量可指向整型变量.字符型.数组一样,这里是指向函数.如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址.有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的.函数指针有两个用途:调用函数和做函数的参数. 1 #include<stdio.h> 2 int max(int

c++11: trailing return type in functions(函数返回类型后置)

In C++03, the return type of a function template cannot be generalized if the return type relies on those of the template arguments. Here is an example, mul(T a, T b) is a function template that calculates the product of a and b. Arguments a and b ar

Flask04 后台获取请求数据、视图函数返回类型、前台接受响应数据

1 后台获取请求数据 1.1 提出问题 前台发送请求的方式有哪些 后台如何获取这些请求的参数 1.2 前台发送请求的方式 GET.POST.AJAX 点睛:如果不指定请求方式,浏览器默认使用GET请求 点睛:进入登录页面的请求和提交登录信息的请求使用的路径都是一样的,只不过前往登录页面的请求是GET请求,服务器返回的是一个静态的页面:当录入登录信息点击确定后就会向后台发送一个POST请求,后台经过逻辑处理后,如果登录信息正确就会返回一个静态主页面(注意:虽然这两个请求都是使用的一样的路径,但是我

vector作为函数返回类型

#include <vector> #include <iostream> using namespace std; vector<int> fun1(int num) { vector<int> values; for (int j = 0; j < num; j++) { values.push_back(j); } return values; } int main() { vector<int> myvector; int i; c

Oracle PLSQL Demo - 20.弱类型REF游标[没有指定查询类型,也不指定返回类型]

declare Type ref_cur_variable IS REF cursor; cur_variable ref_cur_variable; v_ename varchar2(10); v_deptno number(2); v_sal number(7,2); v_sql varchar2(100) := 'select t.ename, t.deptno, t.sal from scott.emp t'; begin Open cur_variable For v_sql; Loo

《Effective Modern C++》读书笔记 Item 2 auto的类型推导

注意: 还要学习一个 ↑↑↑↑ 这样的方框里的片段完全不来自于原书,而是我自己的理解. Item 2 Understand auto type deduction - auto类型推导 在C++11之前,auto 关键字一直是用于声明自动储存类型的变量时使用的,基本上没有什么实际作用,地位和 export 关键字(用于向编译单元之外导出模板,也在C++11中被取消)类似. 在C++11中,auto 终于不再废材,终于具备了类似C#中 var 关键字的效果,可以自动推导出变量的类型,可以少打几个字

《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导

条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇.那是什么呢?即模板类型推导包含了模板.函数和參数,而auto类型判断不用与这些打交道. 这当然是真的.可是没关系. 模板类型推导和auto自己主动类型推导是直接匹配的. 从字面上看,就是从一个算法转换到还有一个算法而已. 在条款1中.阐述模板类型推导採用的是常规的函数模板: template<ty