• 第2课 类型推导(2)_decltype关键字


    1. decltype关键字

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

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

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

    //2.1.cpp

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int x = 0;
        
        decltype(x) y = 1;      //y: int
        decltype(x + y) z = 0;  //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 0;
    }

    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 = 0;
    const int& g_ri = g_ci;
    
    const int foo(int){
        return 0;
    }
    
    const int bar(int){
        return 0;
    }
    
    class test
    {
    public:
        test& operator=(const test& rhs)
        {
            var1 = rhs.var1;
            return *this;
        }
        const double foo(int){return 0;}
        const double bar(int){return 0;}
        
        int var1 = 0;
        
        const double var2 = 0;
        static const int n = 0;
    };
    
    int arr[10];
    int&& g_rr=0;  //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 = 0;  //ci: const int;
        decltype(g_ri) ri = ci;  //ri: const int&;
        decltype(arr)  ar = {1,2,3,4,5}; //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)(123);//函数调用
        decltype(&test::foo) ptestbar2 = &test::bar; //pftestbar2类型与pftestbar相同
        (t.*ptestbar2)(123);//函数调用
        
        decltype(test::var1) tvar1 = 0; //tvar1: int
        decltype(t.var1)  tv1 = 0;   //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 = 0; //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(123)) foo1 = 0; //foo1: int,函数调用,返回值为纯右值,cv被丢弃。
        foo1 = 1; //编译通过,证明foo1的const属性被丢弃
        
        decltype(t.foo(123)) tfoo = 0.0; //tfoo: double,返回值为pvalue,cv丢失。
        
        decltype(foo2(123)) lfoo = 0;    //lfoo: const int&, 返回值是左值,cv保留
        //lfoo = 2; //编译不通过,因为lfoo具有const属性。
        
        int i=0;
        int& j=i;
        decltype(++i) i1 = j; //i1: int&,即左值引用,因为++i的最后一步返回i,所以结果是左值。
        decltype(i++) i2 = 0; //i2: int,即纯右值,因为i++的最后一步是自增的结果,其结果是纯右值。
        
        decltype(++j) j1 = i; //j1: int&
        decltype(j++) j2 = 0; //j2: int
            
        decltype(std::move(i)) i3 = 0; //i3:int&&。因为i被move为xvalue。
        
        int m=0,n=0;
        decltype(m+n) mn = 0; //mn: int
        decltype(n +=m) mn2 = m;//mn2: int&。因为n+=m的最后一步赋值并返回n,这个的结果是左值。
        
        decltype(arr[3]) 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(123))) foo3 = 0; //foo3: int。因为foo函数的返回值为const int类型,加括号后表达式类型
                                       //仍为const int类型,是一个prvalue。故推导结果为int
        foo3 = 1;                      //编译通过,表示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 0;
    }

    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 = 0;
    volatile int vi;
    
    struct S
    {
        int i;
    };
    
    const S a = {0};
    
    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 = 1;
        int& j = i;
        int* p = &i;
        const int k = 1;
        
        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 = 1; //var4: const int; 因为decltype(k)推导出来己带const,
                                    //所以最前面的const是冗余的。
        
        
        return 0;
    }
  • 相关阅读:
    sql 中 列转换成拼音首字母简写【邹建版】
    取一个任意数所有 和的等式
    sql 汉字转全拼音(非首字母)
    实现消息来时让网页标题闪动
    hdoj 1754 I Hate It 线段树(二)
    nyoj 247 虚拟城市之旅 路径压缩
    hdoj 1247 字典树分词 strncpy函数
    hdoj 1671字典树水题之三 静态数组节约内存法
    sort函数
    hdoj 1166 排兵布阵 线段树()
  • 原文地址:https://www.cnblogs.com/5iedu/p/6931617.html
Copyright © 2020-2023  润新知