返回类型推导 decltype

decltype 推导规则

编译器将根据以下三条规则得出结果:

  • 如果 exp 是一个不被括号( )包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致,这是最普遍最常见的情况。
  • 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
  • 如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&。
【实例】exp 是左值,或者被( )包围:
using namespace std;
class Base{
public:int x;
};
int main(){const Base obj;//带有括号的表达式decltype(obj.x) a = 0;  //obj.x 为类的成员访问表达式,符合推导规则一,a 的类型为 intdecltype((obj.x)) b = a;  //obj.x 带有括号,符合推导规则三,b 的类型为 int&。//加法表达式int n = 0, m = 0;decltype(n + m) c = 0;  //n+m 得到一个右值,符合推导规则一,所以推导结果为 intdecltype(n = n + m) d = c;  //n=n+m 得到一个左值,符号推导规则三,所以推导结果为 int&return 0;
}

用法

在难以或不可能以标准写法进行声明的类型时,decltype 很有用,例如 lambda 相关类型或依赖于模板形参的类型。C++11新标准引入了decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。

据我自己的理解,这个关键字经常被用在模板编程中,模板在使用时,可能会出现不知道应该声明是什么类型的状况

template<typename T1,typename T2>?type?  fun(T1 a,T2 b){    ?type? aplusb=a+b;    return aplusb;}

在上述情况中,我们事先并不知道aplusb的类型,无法对其进行声明。
但是C++11中新增加的关键字decltype解决了这个问题

template<typename T1,typename T2>auto fun(T1 a,T2 b) ->decltype(a+b) //auto 马上会讲{    return aplusb=a+b;}

为什么函数的返回值是auto嘞?由于在提供返回类型之前,还未声明变量a,b所以无法对返回类型设置为decltype(a,b)
这时候在C++11中提供了一个解决方案,就是后置返回类型。简单的说就是把返回值后置。

例子

#include <iostream>struct A { double x; };const A* a;decltype(a->x) y;       // y 的类型是 double(其声明类型)decltype((a->x)) z = y; // z 的类型是 const double&(左值表达式)???template<typename T, typename U>auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参{                                     // C++14 开始可以推导返回类型    return t+u;}int main() {    int i = 33;    decltype(i) j = i * 2;    std::cout << "i = " << i << ", "              << "j = " << j << '\n';    auto f = [](int a, int b) -> int    {        return a * b;    };    decltype(f) g = f; // lambda 的类型是独有且无名的    i = f(2, 2);    j = g(3, 3);    std::cout << "i = " << i << ", "              << "j = " << j << '\n';}

输出:

i = 33, j = 66i = 4, j = 9

decltype 的实际应用

auto 的语法格式比 decltype 简单,所以在一般的类型推导中,使用 auto 比使用 decltype 更加方便,本节仅演示只能使用 decltype 的情形。
我们知道,auto 只能用于类的静态成员,不能用于类的非静态成员(普通成员),如果我们想推导非静态成员的类型,这个时候就必须使用 decltype 了。下面是一个模板的定义:


#include <vector>
using namespace std;template <typename T>
class Base {
public:void func(T& container) {m_it = container.begin();
}private:typename T::iterator m_it; //注意这里
};int main()
{const vector<int> v;Base<const vector<int>> obj;obj.func(v);return 0;
}

单独看 Base 类中 m_it 成员的定义,很难看出会有什么错误,但在使用 Base 类的时候,如果传入一个 const 类型的容器,编译器马上就会弹出一大堆错误信息。原因就在于,T::iterator并不能包括所有的迭代器类型,当 T 是一个 const 容器时,应当使用 const_iterator。

要想解决这个问题,在之前的 C++98/03 版本下只能想办法把 const 类型的容器用模板特化单独处理,增加了不少工作量,看起来也非常晦涩。但是有了 C++11 的 decltype 关键字,就可以直接这样写:

template <typename T>
class Base {
public:void func(T& container) {m_it = container.begin();
}private:decltype(T().begin()) m_it; //注意这里
};

看起来是不是很清爽?

注意,有些低版本的编译器不支持T().begin()这种写法,以上代码我在 VS2019 下测试通过,在 VS2015 下测试失败。

类型推导之 auto

用法

C++11中,auto不再是一个存储类型指示符,而是一个自动推导变量的类型。auto并非是一种类型的声明,而是一个类型声明时的“占位符”编译器在编译期间会将auto替换为变量实际的类型

我觉得auto给我们带来的最大改变有两方面。

  1. 解放程序员双手。有了auto这个神器,妈妈再也不用担心我的键盘手了。auto可以代替以前要打的很长很长的变量类型

  2. 还是用在模板推导,下边的例子如果没有auto,x*y就没有办法填写类型了。

template <typename _Tx,typename _Ty>void Multiply(_Tx x, _Ty y){    auto v = x*y;    std::cout << v;}

需要注意的是:

  • auto 变量必须在定义时初始化,这类似于const关键字。

  • 如果初始化表达式是引用,则去除引用语义。

  • 如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。

  • 如果auto关键字带上&号,则不去除const语意。

const int a2 = 10;auto &b2 = a2;//因为auto带上&,故不去除const,b2类型为const int
  • 初始化表达式为数组时,auto关键字推导类型为指针。

  • 若表达式为数组且auto带上&,则推导类型为数组类型。

  • 函数或者模板参数不能被声明为auto

void func(auto a)  //错误{    //... }
  • 时刻要注意auto并不是一个真正的类型。auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。

cout << sizeof(auto) << endl;//错误cout << typeid(auto).name() << endl;//错误

例子

#include <iostream>#include <utility>template<class T, class U>auto add(T t, U u) { return t + u; } // 返回类型是 operator+(T, U) 的类型// 在其所调用的函数返回引用的情况下// 函数调用的完美转发必须用 decltype(auto)template<class F, class... Args>decltype(auto) PerfectForward(F fun, Args&&... args) {     return fun(std::forward<Args>(args)...); }template<auto n> // C++17 auto 形参声明auto f() -> std::pair<decltype(n), decltype(n)> // auto 不能从花括号初始化器列表推导{    return {n, n};}int main(){    auto a = 1 + 2;            // a 的类型是 int    auto b = add(1, 1.2);      // b 的类型是 double    static_assert(std::is_same_v<decltype(a), int>);    static_assert(std::is_same_v<decltype(b), double>);    auto c0 = a;             // c0 的类型是 int,保有 a 的副本    decltype(auto) c1 = a;   // c1 的类型是 int,保有 a 的副本    decltype(auto) c2 = (a); // c2 的类型是 int&,为 a 的别名    std::cout << "a, before modification through c2 = " << a << '\n';    ++c2;    std::cout << "a, after modification through c2 = " << a << '\n';    auto [v, w] = f<0>(); // 结构化绑定声明    auto d = {1, 2}; // OK:d 的类型是 std::initializer_list<int>    auto n = {5};    // OK:n 的类型是 std::initializer_list<int>//  auto e{1, 2};    // C++17 起错误,之前为 std::initializer_list<int>    auto m{5};       // OK:C++17 起 m 的类型为 int,之前为 initializer_list<int>//  decltype(auto) z = { 1, 2 } // 错误:{1, 2} 不是表达式    // auto 常用于无名类型,例如 lambda 表达式的类型    auto lambda = [](int x) { return x + 3; }; //  auto int x; // 于 C++98 合法,C++11 起错误//  auto x;     // 于 C 合法,于 C++ 错误}

可能的输出:

a, before modification through c2 = 3a, after modification through c2 = 4

区别

auto 和 decltype 关键字都可以自动推导出变量的类型,但它们的用法是有区别的:

auto varname = value;
decltype(exp) varname = value;

其中,varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式。

auto 根据=右边的初始值 value 推导出变量的类型,而 decltype 根据 exp 表达式推导出变量的类型,跟=右边的 value 没有关系。 

decltype 和 auto相关推荐

  1. C++11新增的关键字decltype(auto)

    一.函数模板存在的类型问题 template<class T1,class T2> void fun(T1 a, T2 b) {(T1 or T2?)y = a + b; } 语句y = ...

  2. 【C++】五分钟快速理解decltype与auto

    首先来看下这样一段简单的代码: #include <iostream>template <class s1,class s2> typename sum(s1 x, s2 y) ...

  3. C++11新特性——auto和decltype

    一.auto和decltype 1.auto和decltype都是C++11新增的关键字,都用于类型自动推导. 类型格式 auto varname = value; //auto的语法格式 declt ...

  4. (5)风色从零单排《C++ Primer》 const,typedef,auto,decltype

    从零单排<C++ Primer> --(5)const,typedef,auto,decltype   CONST 多文件下 //file_1.cc defines and initial ...

  5. 有auto为什么还要decltype ?详解decltype的用法

    decltype用法 auto和decltype推导类型的区别 decltype用法: 1.decltype变量 2.==decltype表达式== 2.1 表达式做右值 2.2 表达式能做左值 3. ...

  6. C++之(泛型编程基础)auto、decltype

    auto 编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型.然而要做到这一点并非那么容易,有时甚至根本做不到.为了解决这个问题,C++11 新标准引入了 auto类型 ...

  7. C++11 类型推导decltype

    我们之前使用的typeid运算符来查询一个变量的类型,这种类型查询在运行时进行.RTTI机制为每一个类型产生一个type_info类型的数据,而typeid查询返回的变量相应type_info数据,通 ...

  8. C++ decltype类型说明符(尾置返回类型使用)

    转自https://blog.csdn.net/yhl_leo/article/details/50865552 1 基本语法 decltype 类型说明符生成指定表达式的类型.在此过程中,编译器分析 ...

  9. 关键字typedef、关键字using、auto类型说明符和declytpe类型指示符

    文章目录 类型别名 概念 关键字 typedef 别名声明 (alias declaration) using 指针.常量和类型别名 类型别名简化多维数组指针 auto类型说明符 概念 复合类型.常量 ...

最新文章

  1. 因为 Java 和 Php 在获取客户端 cookie 方式不同引发的 bug
  2. 计算机语言XML,在计算机中有一门语言叫“XML”,请问它的全称是什么?是什么含义?...
  3. MySQL 备份与主从复制
  4. python学习-字符串的基本操作
  5. python gui界面启动脚本、点击按钮执行脚本_如何使用PyQt在按钮单击上调用python脚本...
  6. Django---Cookie Session 分页
  7. 读取XML文件报 - Content is not allowed in prolog
  8. 微信防撤回python代码_python实现微信防撤回神器
  9. 解决:Ubuntu18.04配置exoprt LC_ALL=C后无法打开终端
  10. UML设计中的箭头详解
  11. selenium3下打不开Firefox报错解决
  12. applicationcontext获取bean_如果你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章...
  13. Windows照片查看器
  14. Matlab+cpp矩量法代码演示
  15. 团体程序设计天梯赛——L1-039 古风排版
  16. mysql误删数据恢复操作
  17. 地铁3D可视化,让一切尽在掌握
  18. MATLAB中能对三角函数降幂嘛,初中数学三角函数降幂公式
  19. 将SSH多次登录失败的IP加入黑名单
  20. 【有利可图网】PS实战系列:果汁喷溅的效果,你知道是怎么做的吗?

热门文章

  1. 045魔法方法:属性访问
  2. 【转】【Asp.Net】Asp.net发送邮件的两种方法小结
  3. SharePoint 2010 隐藏快速启动栏(左侧导航)
  4. Msfvenonm生成后门
  5. Python简单实现基于VSM的余弦相似度计算
  6. HarmonyOS之数据管理·轻量级偏好数据库的应用
  7. 【数据结构与算法】之深入解析“打家劫舍”的求解思路与算法示例
  8. 2020年第十一届蓝桥杯 - 省赛 - Python大学组 - G. 单词分析
  9. 数据库开发——MySQL——数据类型——非数值类型
  10. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1065:奇数求和