1. decltype关键字

(1)auto所修饰的变量必须被初始化,编译器才能通过初始化来确定auto所代表的类型,即必须先定义变量。

(2)decltype可以在编译期推导出一个变量或表达式的结果类型(但不会真正计算表达式的值),并且使用这个结果定义新的变量。

【实例分析】获取表达式的类型

//2.1.cpp

#include <iostream>
using namespace std; int main()
{
int x = ; decltype(x) y = ; //y: int
decltype(x + y) z = ; //z: int const int& i = x;
decltype(i) j = y; //j: const int&,保留cv和引用属性 const decltype(z)* p = &z; //p: const int*,
//decltype(z)*在z的类型基础上加个* //const auto* p = &z; //p: const int*,此处auto*中的*是冗余的
//相当于const auto p = &z; decltype(z)* pi = &z; //pi: int*
decltype(pi)* pp = &pi; //在pi类型的基础上加个*,即pp:int** return ;
}

2.decltype(exp)的推导规则(依序判断)

(1)规则1:如果exp是一个简单的标记符表达式或者类成员访问表达式,则decltype(exp)的类型就是exp的类型,包含的cv修饰符也不会丢失。(注意,当exp是一个被重载的函数则编译不过)。

  ①标记符及标记符表达式:标记符——除去关键字、字面量等标记之外、由程序自定义的标记(token)都是标记符(identifier)。而由单个标识符对应的表达式就是标记符表达式

  ②如,int arr[4]中arr是一个标记符表达式,但arr[3]+0,arr[3]都不是标记符表达式。同理,int a,a是一个标记符表达式,而(a)或a+1都不是标记符表达式,其中(a)是一个左值表达式,而a+1是个右值表达式。、

(2)规则2:如果exp不是简单的标记符表达式则必是xvalue(将亡值,右值中的一种。如std::move返回值、T&&函数返回值)、prvalue(纯右值:如非引用返回的对象、表达式产生的临时对象)、 lvaue(左值)三者之一。(注意cv修饰符可能会丢弃!)

  ①if exp==>lvalue then decltype(exp) ==>T& (假设exp的类型为T)

  ②if exp==>prvalue then decltype(exp) ==>T (重要提示:对于纯右值而言,只有类类型可以保留cv限定符,其它类型则会丢失cv限定)

  ③if exp==>xvalue then decltype(exp) ==>T&&

(3)规则3:如果exp被加上括号,则decltype不再认为exp是个简单的标识符表达式,然后按照规则2推理。

【实例分析】decltype的推导规则

2.2.cpp

#include <iostream>

using namespace std;

const int g_ci = ;
const int& g_ri = g_ci; const int foo(int){
return ;
} const int bar(int){
return ;
} class test
{
public:
test& operator=(const test& rhs)
{
var1 = rhs.var1;
return *this;
}
const double foo(int){return ;}
const double bar(int){return ;} int var1 = ; const double var2 = ;
static const int n = ;
}; int arr[];
int&& g_rr=; //g_rr: int&&(纯右值)
int* ptr = arr; test t; const int& foo2(int){
static int kk;
return kk;
} const test testfunc(){
//return test();
} int main()
{
//////////////////////////////////////////////////////////////////////////////////////
//规则1:简单标记符表达式和类成员表达式
decltype(g_ci) ci = ; //ci: const int;
decltype(g_ri) ri = ci; //ri: const int&;
decltype(arr) ar = {,,,,}; //ar: int[5] decltype(foo)* pfbar = bar; //pfbar:const int (*)(int),函数指针
decltype(&foo) pfbar2 = bar; //pfbar2类型与pfbar相同 decltype(test::foo) ptestbar = test::bar; //pftestbar: const double (test::*)(int);
(t.*ptestbar)();//函数调用
decltype(&test::foo) ptestbar2 = &test::bar; //pftestbar2类型与pftestbar相同
(t.*ptestbar2)();//函数调用 decltype(test::var1) tvar1 = ; //tvar1: int
decltype(t.var1) tv1 = ; //tv1: int
decltype(&test::var1) ptv1 =&test::var1; //ptv1: int test::*,可t.*ptv1=2方式来访问。 decltype(test::var2) tvar2 = 0.0; //tvar2: const double
decltype(t.var2) tv2 = 0.0; //tv2: const double;
decltype(&test::var2) ptv2 =&test::var2; //ptv2: const double test::* decltype(t.n) tn = ; //tn: const int; //////////////////////////////////////////////////////////////////////////////////////
//规则2:lvalue,xvalue,pvalue表达式规则
//如果一个表达式不是“简单id-expression”,则e必是三者之一.
//IF e ==> lvalue THEN decltype(e) ==> T&
//IF e ==> prvalue THEN decltype(e) ==> T //重要提示:e为普通类型时,推导结果cv丢失。e为类类型时则保留cv
//IF e ==> xvalue THEN decltype(e) ==> T&& //函数调用,取返回值类型
decltype(foo()) foo1 = ; //foo1: int,函数调用,返回值为纯右值,cv被丢弃。
foo1 = ; //编译通过,证明foo1的const属性被丢弃 decltype(t.foo()) tfoo = 0.0; //tfoo: double,返回值为pvalue,cv丢失。 decltype(foo2()) lfoo = ; //lfoo: const int&, 返回值是左值,cv保留
//lfoo = 2; //编译不通过,因为lfoo具有const属性。 int i=;
int& j=i;
decltype(++i) i1 = j; //i1: int&,即左值引用,因为++i的最后一步返回i,所以结果是左值。
decltype(i++) i2 = ; //i2: int,即纯右值,因为i++的最后一步是自增的结果,其结果是纯右值。 decltype(++j) j1 = i; //j1: int&
decltype(j++) j2 = ; //j2: int decltype(std::move(i)) i3 = ; //i3:int&&。因为i被move为xvalue。 int m=,n=;
decltype(m+n) mn = ; //mn: int
decltype(n +=m) mn2 = m;//mn2: int&。因为n+=m的最后一步赋值并返回n,这个的结果是左值。 decltype(arr[]) ar3 = m; //ar3: int&,因为arr[3]可以被赋值,是个左值
decltype(*ptr) ptri = m; //ptri: int&,因为*ptr可以被赋值,是个左值。 decltype("string") str = "string"; //str: const char(&)[7],因为字符串字面量为左值。 //////////////////////////////////////////////////////////////////////////////////////
//规则3:带括号表达式规则
decltype((i)) i4 = j; //i4: int&。因为,i是个表达式,(i)也是个(左值)表达式。
decltype((foo())) foo3 = ; //foo3: int。因为foo函数的返回值为const int类型,加括号后表达式类型
//仍为const int类型,是一个prvalue。故推导结果为int
foo3 = ; //编译通过,表示foo3丢弃了const属性
decltype((testfunc())) tfunc; //tfunc: const test,因为testfunc函数返回值为const test类型,是一个prvalue。
//加括号后的表达式仍为prvalue,但由于是类类型,故要保留const属性。 cout <<is_const<decltype(tfunc)>::value<<endl; //输出1,表示tfunc保留了const属性。 //decltype((t.var1)) tv; //tv: int&。因为t.var1是个左值。推导结果为左值引用,即int&。但该行会产生编译错误,
//因为声明引用的同时,必须初始化
cout <<is_lvalue_reference<decltype((t.var1))>::value<<endl; //输出1,是一个左值引用 return ;
}

3.cv限制符的继承与冗余

差异

auto

decltype

const

volatile

①auto var(一般声明时)会可能舍弃cv。

②auto& var或auto* var(带引用或指针时)时,会保留cv属性

一般会保留cv限定符(注意,如果表达式为纯右值时,则可能丢失cv,见前面的推导规则

引用

int& b = a

①auto var = b //var:int,会舍弃引用

②auto& var =a //var:int&,要使auto变量成为另一个变量的引用,必须使用auto&

保留引用,如

decltype(b) c=a; //c:int&

decltype(a)& c=a;//c:int&

指针

int*p = &a

auto* var=p;//var: int*,注意看的是初始化表达式的类型,auto*中的*是冗余的,会被忽略掉(但注意会保留CV)。与auto var=p的声明结果是一样的。

auto* var=&p;//var:int**,原理同上。

decltype(p)* pp;//pp:int**,注意,与auto*不同decltype(p)*的*指的是p的类型的指针形式。

【实例分析】cv继承与冗余

//2.3.cpp

#include <iostream>
using namespace std; const int ci = ;
volatile int vi; struct S
{
int i;
}; const S a = {}; volatile S b;
volatile S* p = &b; int main()
{
///////////////////////////////////////////////////////////
//const对象,使用decltype进行推导时,其成员不会继承const或volatile限定符
//普通对象
cout << is_const<decltype(ci)>::value << endl; //1,是const对象
cout << is_volatile<decltype(vi)>::value << endl; //1, 具有volatile属性 //类对象
cout << is_const<decltype(a)>::value << endl; //1,是const对象
cout << is_volatile<decltype(b)>::value << endl; //1, 具有volatile属性 cout << is_const<decltype(a.i)>::value << endl; //0,const属性不会继承给成员变量
cout << is_volatile<decltype(p->i)>::value << endl; //0, volatile属性不会继承给成员变量 //cv限定符的冗余:通常情况下,如果推导出的类型己经有cv属性,则冗余的符号会被忽略。
//注意,auto一般会丢弃cv,而decltype一般会保留cv。
int i = ;
int& j = i;
int* p = &i;
const int k = ; decltype(i)& var1 = i; //var1: int&
decltype(j)& var2 = i; //var2: int&。j中多出一个&,则冗余的&被忽略
cout << is_rvalue_reference<decltype(var2)>::value << endl;//0,var2不是右值引用,即不是int&&
cout << is_lvalue_reference<decltype(var2)>::value << endl;//1,var2是左值引用,即int& //decltype(p)* var3 = &i; //var3:int**,与&i类型不匹配,编译不通过。
decltype(p)* var3 = &pl; //var3: int**。注意,decltype(p)*中的*不是冗余的!!! auto* v3 = p; //v3: int*。注意auto*中的*是冗余的,相当于auto v3 = p; const decltype(k) var4 = ; //var4: const int; 因为decltype(k)推导出来己带const,
//所以最前面的const是冗余的。 return ;
}

第2课 类型推导(2)_decltype关键字的更多相关文章

  1. 第1课 类型推导(1)_auto关键字

    1.  auto关键字 (1)auto的作用是让编译器自动推断变量的类型,而不需要显式指定类型.这种隐式类型的推导发生在编译期. (2)auto并不能代表实际的类型声明,只是一个类型声明的“占位符” ...

  2. 第4课 decltype类型推导

    第4课 decltype类型推导 一.decltype类型推导 (一)语法: 1.语法:decltype(expr),其中的expr为变量(实体)或表达式 2.说明: ①decltype用于获取变量的 ...

  3. C++11 - 类型推导auto关键字

    在C++11中,auto关键字被作为类型自动类型推导关键字 (1)基本用法 C++98:类型 变量名 = 初值;   int i = 10; C++11:auto 变量名 = 初值;  auto i ...

  4. 第3课 auto类型推导(2)

    第3课 auto类型推导(2) 一.使用auto的优势 (一)避免使用未初始化变量 (二)可简化变量/对象类型的声明 (三) 在某些场合无法判断出类型时,可用auto自动推导(如lambda表达式) ...

  5. 第2课 auto类型推导(1)

    第2课 auto类型推导(1) 一.auto类型推导 (一)与模板类型推导映射关系 1.auto类型推导与模板类型推导可以建立一一映射关系,它们之间存在双向的算法变换.auto扮演模板中T的角色,而变 ...

  6. c++相关的类型推导

    c++11和boost库增加许多关于类型推导(编译期)的关键字和类型, 用好这些机制, 对于编写项目的一些组件帮助颇大.正所谓工欲善其事,必先利其器. 1.初始化某种类型的变量 auto var = ...

  7. Swift学习(三)类型推导&基本运算&分支&循环

    一.Swift中类型推导&基本运算 Swift中类型推导 如果一个标识符在定义时有直接赋值,那么可以根据后面赋值的类型,来推导出前面标识符的类型,这样前面标识符的(:类型)可以省略 查看标识符 ...

  8. C++11类型推导

    [C++11类型推导] auto 关键字.这会依据该初始化子(initializer)的具体类型产生参数: 除此之外,decltype 能够被用来在编译期决定一个表示式的类型. 参考:http://z ...

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

    条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇 ...

随机推荐

  1. Python基础_私有变量访问限制

    Python内置了一些特殊变量,以前后上下划线标注,同时我们自己要想定义一些变量,不想让外部访问,又该怎么做呢?更多内容请参考:Python学习指南 访问限制 在class内部,可以有属性和方法,而外 ...

  2. day2 大纲笔记

    1, pycharm的使用2,昨日内容回顾(作业讲解) 编译型 解释型 python2x: 混乱 冗余 python3x: 规范,清晰 变量: 常量:一直不变的量.全部大写的变量 常量. 注释: # ...

  3. Update 20180317

    Date todo 20180317 Opencv a)         Install opencv+python+ide (http://opencv-python-tutroals.readth ...

  4. [转]HashMap,LinkedHashMap,TreeMap的区别

    Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复. Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很 ...

  5. es高级部分

    1 关于机器 配置. 内存:上亿的数据一般需要64G内存的服务器.劲量不要使用小于32G 内存的服务器. cpu:es 对cpu 要求依赖不如内存.一般要求2-8 核就可以了. 磁盘:es 对磁盘依赖 ...

  6. PHP 换行符 PHP_EOL

    PHP 中换行可以用 PHP_EOL 来替代,以提高代码的源代码级可移植性: unix系列用 \n windows系列用 \r\n mac用 \r <?php echo PHP_EOL; //w ...

  7. itertools.groupby()/itertools.compress() 笔记

    关于itertools.groupby() itertools.groupby()就是将相邻的并且相同的键值划分为同一组,相似功能可以看https://docs.python.org/3/librar ...

  8. spring邮件发送

    1,Emaill类: package com.learn.jsp.pojo; /** * 邮件基本信息 * @author kevin * */public class Email { private ...

  9. Vivado约束文件(XDC)的探究(1)

    Vivado约束文件(XDC)的探究(1) 工程建好之后会出现xdc文件: 注意:active 和 target 生成的约束文件如下:

  10. Go 缓冲信道和非缓冲信道

    非缓冲信道是一个进一个出,再一个进再一个出,信道内是不保存数据的 缓冲信道是可以很多个依次进去,存储在信道里,然后一个一个的按次序取出来. 不过缓冲信道如果超过了预期的存入个数,会发生信道阻塞,只有把 ...