• 【C++0x】表达式之类型(decltype)


     
    C++0x引入了新的关键字decltype,它是一个操作符,用来取得表达式的类型,主要在泛型编程中使用。这里,简单介绍一下语法规则。

    语法形式:decltype (expression)
    其中,这里的括号必不可少(这点不同于sizeof操作符)。decltype(e)可看到是一个类型别名,并且不会对表达式e进行计算(即只有编译时行为而无运行时行为)。另外,不允许把decltype作用于一个类型,因为没有任何理由要这样做。

    确定decltype(e)类型的规则如下
    Rule-1. 如果e是一个标识符表达式或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。如果没有此实体或者e命名了一个重载函数集,那么程序是ill-formed的。
    Rule-2. 如果e是一个函数调用或者一个重载操作符调用(忽略e外面的括号),那么decltype(e)就是该函数的返回类型。
    Rule-3. 否则,假设e的类型是T:如果e是一个左值,则decltype(e)就是T&;否则(e是一个右值),decltype(e)就是T。

    举例分析如下(内容来自参考Ref1):

    eg1 名字空间或局部作用域内的变量(Rule-1)
    int a;
    int& b = a;
    const int& c = a;
    const int d = 5;
    const A e;

    (注:不能直接编译,这里写出来只是分析)
    decltype(a) // int 
    decltype(b) // int&
    decltype(c) // const int&
    decltype(d) // const int
    decltype(e) // const A

    但需要注意括号可能会影响结果,例如:
    decltype((a));  // int& (此时(a)表达式不满足Rule-1和Rule-2,应用Rule-3,而表达式(a)是一个左值,所以为int&)

    eg2 函数形参(Rule-1)
    void foo(int a, int& b, float&& c, int* d)
    {
        decltype(a) // int
        decltype(b) // int&
        decltype(c) // float&&
        decltype(d) // int*
    }

    eg3 函数类型(Rule-1)
    int foo(char);
    int bar(char);
    int bar(int);

    decltype(foo) // int(char)
    decltype(bar) // error, bar is overloaded

    但需要注意当形成函数指针时适用Rule-3:
    decltype(&foo) // int(*)(char)
    decltype(*&foo) // int(&)(char)

    eg4 数据类型(Rule-1)
    int a[10];
    decltype(a)  // int[10]

    eg5 成员变量(Rule-1)
    class A {
        int a;
        int& b;
        static int c;
        
        void foo() {
            decltype(a)          // int
            decltype(this->a)    // int
            decltype((*this).a)  // int
            decltype(b)          // int&
            decltype(c)          // int (static members are treated as variables in namespace scope)
        }
        void bar() const {
            decltype(a)   // int
            decltype(b)   // int&
            decltype(c)   // int
        }
    };

    A aa;
    const A& caa = aa;
    decltype(aa.a)  // int
    decltype(aa.b)   // int&
    decltype(caa.a)  // int

    内置操作符.*和->*适用Rule-3
    decltype(aa.*&A::a) // int&
    decltype(aa.*&A::b) // illegal, cannot take the address of a reference member
    decltype(caa.*&A::a) // const int&

    eg6 this(Rule-3)
    class X {
        void foo() {
            decltype(this)    // X*,因为this是右值
            decltype(*this)   // X&,因为*this是左值
        }
        void bar() const {
            decltype(this)   // const X*
            decltype(*this)  // const X&
        }
    };

    eg7 指向成员变量和成员函数的指针(Rule-1)
    class A {
        int x;
        int& y;
        int foo(char);
        int& bar() const;
    };

    decltype(&A::x)    // int A::*
    decltype(&A::y)    // error: pointers to reference members are disallowed (8.3.3 (3))
    decltype(&A::foo) // int (A::*) (char)
    decltype(&A::bar) // int& (A::*) () const

    eg8 字面值(Rule-3)
    (字符串字面值是左值,其它字面值都是右值)
    decltype("decltype") // const char(&)[9]
    decltype(1) // int

    eg9 冗余的引用符(&)和CV修饰符
    由于decltype表达式是一个类型别名,因此冗余的引用符(&)和CV修饰符被忽略:
    int& i = ...;
    const int j = ...;
    decltype(i)&         // int&. The redundant & is ok
    const decltype(j)   // const int. The redundant const is ok

    eg10 函数调用(Rule-2)
    int foo();
    decltype(foo())    // int
    float& bar(int);
    decltype (bar(1))  // float&
    class A { ... };
    const A bar();
    decltype (bar())    // const A
    const A& bar2();
    decltype (bar2())  // const A&

    eg11 内置操作符(Rule-3)
    decltype(1+2)     // int (+ returns an rvalue)
    int* p;
    decltype(*p)        // int& (* returns an lvalue)
    int a[10];
    decltype(a[3]);     // int& ([] returns an lvalue)
    int i; int& j = i;
    decltype (i = 5)   // int&, because assignment to int returns an lvalue
    decltype (j = 5)   // int&, because assignment to int returns an lvalue
    decltype (++i);    // int&
    decltype (i++);    // int (rvalue)

    如何用程序验证decltype的结果?可以参考下面的程序对上面的分析结果进行验证:
    F: mp>type decltype_eg1.cpp
    #include <iostream>
    #include <string>
    using namespace std;

    template <typename T>
    string Foo()
    {
        return "unknown";
    }

    template <>
    string Foo<int>()
    {
        return "int";
    }

    template <>
    string Foo<const int>()
    {
        return "const int";
    }

    template <>
    string Foo<int &>()
    {
        return "int&";
    }

    template <>
    string Foo<const int&>()
    {
        return "const int&";
    }

    class A{};

    template <>
    string Foo<A>()
    {
        return "A";
    }

    int main()
    {
        int a;
        int &b = a;
        const int &c = a;
        const int d = 5;
        A e;
        double f;

        cout << "a: " << Foo<decltype(a)>() << endl;
        cout << "b: " << Foo<decltype(b)>() << endl;
        cout << "c: " << Foo<decltype(c)>() << endl;
        cout << "d: " << Foo<decltype(d)>() << endl;
        cout << "e: " << Foo<decltype(e)>() << endl;
        cout << "f: " << Foo<decltype(f)>() << endl;
    }


    F: mp>g++ decltype_eg1.cpp -std=c++0x

    F: mp>a.exe
    a: int
    b: int&
    c: const int&
    d: const int
    e: A
    f: unknown

    F: mp>gcc --version
    gcc (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  • 相关阅读:
    asp.net core过滤器记录响应对象
    ef core实现无感知软删除
    Egret资源跨域问题
    ASP.Net Core中使用jquery-ajax-unobtrusive替换Ajax.BeginForm
    把.Net开发环境迁移到Linux上去
    Mysql8.0升级后,Navicat连接报错caching_sha2_password 问题
    改MySQL的编码方式,解决jdbc MySQL中文乱码问题
    怡红公子专属网址导航
    html以前没有学到的标签
    有哪些质量上乘的程序员必关注的网站或论坛
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3752367.html
Copyright © 2020-2023  润新知