decltype
在某些情况下,不需要或者不能定义变量,但是希望得到某种类型,这时候就可以使用 C++11 提供的 decltype 关键字了,它的作用是在编译器编译的时候推导出一个表达式的类型,语法格式如下:

C++ decltype (表达式)
decltype 是 “declare type” 的缩写,意思是 “声明类型”。decltype 的推导是在编译期完成的,它只是用于表达式类型的推导,并不会计算表达式的值。来看一组简单的例子:

int a = 10;
decltype(a) b = 99;                 // b -> int
decltype(a+3.14) c = 52.13;         // c -> double
decltype(a+b*c) d = 520.1314;       // d -> double

可以看到 decltype 推导的表达式可简单可复杂,在这一点上 auto 是做不到的,auto 只能推导已初始化的变量类型。

2.1 推导规则
通过上面的例子我们初步感受了一下 decltype 的用法,但不要认为 decltype 就这么简单,在它简单的背后隐藏着很多的细节,下面分三个场景依次讨论一下:

1. 表达式为普通变量或者普通表达式或者类表达式,在这种情况下,使用 decltype 推导出的类型和表达式的类型是一致的。

#include <iostream>
#include <string>
using namespace std;class Test
{public:string text;static const int value = 110;
};int main()
{int x = 99;const int &y = x;decltype(x) a = x;decltype(y) b = x;decltype(Test::value) c = 0;Test t;decltype(t.text) d = "hello, world";return 0;
}
变量 a 被推导为 int 类型
变量 b 被推导为 const int & 类型
变量 c 被推导为 const int 类型
变量 d 被推导为 string 类型

2. 表达式是函数调用,使用 decltype 推导出的类型和函数返回值一致。

class Test{...};
//函数声明
int func_int();                 // 返回值为 int
int& func_int_r();              // 返回值为 int&
int&& func_int_rr();            // 返回值为 int&&const int func_cint();          // 返回值为 const int
const int& func_cint_r();       // 返回值为 const int&
const int&& func_cint_rr();     // 返回值为 const int&&const Test func_ctest();        // 返回值为 const Test//decltype类型推导
int n = 100;
decltype(func_int()) a = 0;
decltype(func_int_r()) b = n;
decltype(func_int_rr()) c = 0;
decltype(func_cint())  d = 0;
decltype(func_cint_r())  e = n;
decltype(func_cint_rr()) f = 0;
decltype(func_ctest()) g = Test();
变量 a 被推导为 int 类型
变量 b 被推导为 int& 类型
变量 c 被推导为 int&& 类型
变量 d 被推导为 int 类型
变量 e 被推导为 const int & 类型
变量 f 被推导为 const int && 类型
变量 g 被推导为 const Test 类型

函数 func_cint () 返回的是一个纯右值(在表达式执行结束后不再存在的数据,也就是临时性的数据),对于纯右值而言,只有类类型可以携带const、volatile限定符,除此之外需要忽略掉这两个限定符,因此推导出的变量 d 的类型为 int 而不是 const int。

3. 表达式是一个左值,或者被括号 ( ) 包围,使用 decltype 推导出的是表达式类型的引用(如果有 const、volatile 限定符不能忽略)。

#include <iostream>
#include <vector>
using namespace std;class Test
{public:int num;
};int main() {const Test obj;//带有括号的表达式decltype(obj.num) a = 0;decltype((obj.num)) b = a;//加法表达式int n = 0, m = 0;decltype(n + m) c = 0;decltype(n = n + m) d = n;return 0;
}
obj.num 为类的成员访问表达式,符合场景 1,因此 a 的类型为 int
obj.num 带有括号,符合场景 3,因此 b 的类型为 const int&。
n+m 得到一个右值,符合场景 1,因此 c 的类型为 int
n=n+m 得到一个左值 n,符合场景 3,因此 d 的类型为 int&

2.2 decltype 的应用
关于 decltype 的应用多出现在泛型编程中。比如我们编写一个类模板,在里边添加遍历容器的函数,操作如下:

#include <list>
using namespace std;template <class T>
class Container
{public:void func(T& c){for (m_it = c.begin(); m_it != c.end(); ++m_it){cout << *m_it << " ";}cout << endl;}
private:??? m_it;  // 这里不能确定迭代器类型
};int main()
{const list<int> lst;Container<const list<int>> obj;obj.func(lst);return 0;
}

在程序的第 17 行出了问题,关于迭代器变量一共有两种类型:只读(T::const_iterator)和读写(T::iterator),有了 decltype 就可以完美的解决这个问题了,当 T 是一个 非 const 容器得到一个 T::iterator,当 T 是一个 const 容器时就会得到一个 T::const_iterator。

#include <list>
#include <iostream>
using namespace std;template <class T>
class Container
{public:void func(T& c){for (m_it = c.begin(); m_it != c.end(); ++m_it){cout << *m_it << " ";}cout << endl;}
private:decltype(T().begin()) m_it;  // 这里不能确定迭代器类型
};int main()
{const list<int> lst{ 1,2,3,4,5,6,7,8,9 };Container<const list<int>> obj;obj.func(lst);return 0;
}

decltype(T().begin()) 这种写法在 vs2017/vs2019 下测试可用完美运行。

3. 返回类型后置
在泛型编程中,可能需要通过参数的运算来得到返回值的类型,比如下面这个场景:

#include <iostream>
using namespace std;
// R->返回值类型, T->参数1类型, U->参数2类型
template <typename R, typename T, typename U>
R add(T t, U u)
{return t + u;
}int main()
{int x = 520;double y = 13.14;// auto z = add<decltype(x + y), int, double>(x, y);auto z = add<decltype(x + y)>(x, y);    // 简化之后的写法cout << "z: " << z << endl;return 0;
}

关于返回值,从上面的代码可以推断出和表达式 t+u 的结果类型是一样的,因此可以通过通过 decltype 进行推导,关于模板函数的参数 t 和 u 可以通过实参自动推导出来,因此在程序中就也可以不写。虽然通过上述方式问题被解决了,但是解决方案有点过于理想化,因为对于调用者来说,是不知道函数内部执行了什么样的处理动作的。

因此如果要想解决这个问题就得直接在 add 函数身上做文章,先来看第一种写法:

template <typename T, typename U>
decltype(t+u) add(T t, U u)
{return t + u;
}

当我们在编译器中将这几行代码改出来后就直接报错了,因此 decltype 中的 t 和 u 都是函数参数,直接这样写相当于变量还没有定义就直接用上了,这时候变量还不存在,有点心急了。
在C++11中增加了返回类型后置语法,说明白一点就是将decltype和auto结合起来完成返回类型的推导。其语法格式如下:

// 符号 -> 后边跟随的是函数返回值的类型
auto func(参数1, 参数2, ...) -> decltype(参数表达式)

C++14中可以直接使用

template<typename T, typename U>
auto add3(T x, U y){return x + y;
}

通过对上述返回类型后置语法代码的分析,得到结论:auto 会追踪 decltype() 推导出的类型,因此上边的 add() 函数可以做如下的修改:

#include <iostream>
using namespace std;template <typename T, typename U>
// 返回类型后置语法
auto add(T t, U u) -> decltype(t+u)
{return t + u;
}int main()
{int x = 520;double y = 13.14;// auto z = add<int, double>(x, y);auto z = add(x, y);      // 简化之后的写法cout << "z: " << z << endl;return 0;
}

为了进一步说明这个语法,我们再看一个例子:

#include <iostream>
using namespace std;int& test(int &i)
{return i;
}double test(double &d)
{d = d + 100;return d;
}template <typename T>
// 返回类型后置语法
auto myFunc(T& t) -> decltype(test(t))
{return test(t);
}int main()
{int x = 520;double y = 13.14;// auto z = myFunc<int>(x);auto z = myFunc(x);             // 简化之后的写法cout << "z: " << z << endl;// auto z = myFunc<double>(y);auto z1 = myFunc(y);            // 简化之后的写法cout << "z1: " << z1 << endl;return 0;
}

在这个例子中,通过 decltype 结合返回值后置语法很容易推导出来 test(t) 函数可能出现的返回值类型,并将其作用到了函数 myFunc() 上。

// 输出结果
z: 520
z1: 113.14

C++11 decltype表达式相关推荐

  1. c++11 常量表达式

    c++11 常量表达式 #define _CRT_SECURE_NO_WARNINGS#include <iostream> #include <string> #includ ...

  2. C++11 decltype 的用法粗解

    1.decltype 是啥子 感性认识跟 auto 一样 auto a = 1; 但 decltype 是这样用 decltype(表达式) a = 值;// 例如 decltype(1) a = 2 ...

  3. C++11 decltype类型推导详解

    decltype decltype 是 C++11 新增的一个关键字,它和 auto 的功能一样,都用来在编译时期进行自动类型推导. decltype 是"declare type" ...

  4. C++11:Lambda表达式(匿名函数)理解

    C++在C11标准中引入了匿名函数,即没有名字的临时函数,又称之为lambda表达式.lambda表达式 实质上是创建一个匿名函数/对象.即你可以理解为(Lambda 表达式实际上是一个函数,只是它没 ...

  5. python前缀表达式求值_python数据结构与算法 11 后缀表达式求值

    从本节开始,删除原版的英文,直接发译后的文稿. 后缀表达式求值 栈的最一个应用例子,计算一个后缀表达式的值.这个例子中仍然用栈的数据结构.不过,当扫描表达式的时候,这次是操作数压栈等待,不是转换算法中 ...

  6. C++11 Lambda表达式(匿名函数)详解

    使用 STL 时,往往会大量用到函数对象,为此要编写很多函数对象类.有的函数对象类只用来定义了一个对象,而且这个对象也只使用了一次,编写这样的函数对象类就有点浪费. 而且,定义函数对象类的地方和使用函 ...

  7. C++11 lambda表达式、function类模板、bind函数适配器

    文章目录 lambda表达式 lambda表达式的语法 lambda表达式的原理 function模板 function与重载函数 bind函数适配器 lambda表达式 当我们在写代码的时候如果经常 ...

  8. [C++11]lambda表达式语法

    代码如下: #include <iostream> using namespace std;void func(int x, int y) {int a;int b;[=]() {int ...

  9. [C++11]常量表达式函数

    constexpr修饰函数. 普通函数/类成员函数. 1.函数必须要有返回值,并且return返回的表达式必须是常量表达式. 代码如下: #include <iostream> using ...

最新文章

  1. 如何区分惯性器件的零偏误差?
  2. MPB:亚热带生态所谭支良、焦金真等-​反刍动物瘤胃样品采集与保存
  3. C++ 发手机短信(很易很简单)
  4. MNN通用端侧深度学习预测框架介绍
  5. linux 查看链接最终目标,linux学习笔记7-链接
  6. 【JAVA】关于向上转型与向下转型
  7. 修改telnet提示并非_iPhone修改微信提示音,iOS12可用,无需越狱详细教程
  8. avc水平什么意思_5个步骤切实有效地提高你的写作水平
  9. 使用linux批量引物设计,【分享】超实用的引物设计操作,一看就学会
  10. varbinary mysql_MySQL中的数据类型binary和varbinary详解
  11. SPSS统计分析行业应用实战--SPSS 22.0新增
  12. 计算机并口优点,并口光驱优缺点有哪些 并口光驱如何转串口
  13. Day03 爬取京东商品信息+元素交互操作+BeautifulSoup4
  14. AutoJsPro,阿伟的假期(看广告游戏)脚本实例
  15. extract($_POST, EXTR_SKIP)讲解
  16. 第十五周作业——ZJM与生日礼物
  17. 与 BGI 绘图库的兼容情况
  18. 怎么把APP上传到各大安卓应用商店
  19. 通讯中断 pc_《幽灵行动:断点》PC配置需求公布 4K画质需RTX2080
  20. python微服务架构设计模式_微服务架构设计模式 PDF 电子书 百度云 网盘下载

热门文章

  1. windows下styleGAN2和styleGAN3编译配置bug
  2. 仪器测量色差和人眼目视不一样
  3. 五、XMLTomcatHttp协议
  4. jquery.gritter.js简介
  5. 跳板机文件上传至其他服务器
  6. Android 天气预报(2)
  7. 怀旧服美服哪个服务器人最多,魔兽世界怀旧服你排队多久 美服平均4小时是什么水平...
  8. R语言-rnorm函数
  9. Excel 下拉单元格,使整行背景变色(一看就懂)
  10. 程序员10大推荐网站