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:\tmp>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:\tmp>g++ decltype_eg1.cpp -std=c++0x

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

F:\tmp>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.

【C++0x】表达式之类型(decltype)的更多相关文章

  1. LINQ to Entities 不支持 LINQ 表达式节点类型“ArrayIndex”

    我就不屁话,能一张图就解决的就不说话了 2015-03-28 14:53:24,440 [10] ERROR log - System.NotSupportedException: LINQ to E ...

  2. LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”(笔记)

    今天使用使用动态查询的时候出现出现错误“LINQ to Entities 不支持 LINQ 表达式节点类型‘Invoke’.”,代码如下: IQueryable<CUSTOMER> que ...

  3. 无法确定条件表达式的类型,因为“<null>”和“System.DateTime”之间没有隐式转换----解决办法

    例子:(报错了) public DateTime? time { get; set; } time = item.HospOutDate.HasValue ? DateTime.Parse(item. ...

  4. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  5. 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型

    今天写winform的时候遇到一个问题,提示: 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型, 主要是为了在子线程中更新UI线程,在wpf中同样的写法 ...

  6. 工作总结 无法确定条件表达式的类型,因为“<null>”和“System.DateTime”之间没有隐式转换 解决办法 object——Nullable<T> (可空类型)

    可空值类型 备注     一种类型认为是可以为 null,如果它可以分配一个值,也可以分配null,这意味着类型具有无论如何没有值. 默认情况下,所有都引用类型,如String,是否可以为 null, ...

  7. java 运算符使表达式结果类型自动提升

    1.表达式中的自动类型提升: 表达式求值时,Java自动的隐含的将每个byte.short或char操作数提升为int类型,这些类型的包装类型也是可以的. 例如:short s1 = 1; s1 = ...

  8. 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型

    this.BeginInvoke(() => { this.btnQuery.Enabled = false; //禁用查询 }); 跨线程调用时,编译上面的代码将提示 对于Control.In ...

  9. Entity Framework解决sql 条件拼接,完美解决 解决 不支持 LINQ 表达式节点类型“Invoke”【转】

    传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句. 在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如: using(var db=ne ...

随机推荐

  1. Vue 页面15分钟无操作时返回首页

    这种需求手机端和pc端一般是不存在的,因为都是可以手动操作刷新的. 最近在做一个户外社区大屏的项目,因为大屏是全屏显示,没法手动刷新,不可能在页面专门做一个刷新按钮,也不好看,那这样的需求就显得格外重 ...

  2. Flutter 中 ListView 的使用

    这个小例子使用的是豆瓣 API 中 正在上映的电影的开放接口,要实现的主要效果如下: JSON 数据结构 Item 结构 Item 的结构是一个 Card 包含着一个 Row 然后这个 Row 里面左 ...

  3. Mongodb的入门(1)window安装mongodb

    mongodb: Mongodb,分布式文档存储数据库,由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数 ...

  4. Oracle 查询状态 自检

    Tips:fnd_lobs表会保存我们上传的一些文件和Form界面“文件“-“导出”的文件.如果不定期清理了话,会出现文件上传失败,或者是导出按钮可以点击,但是点击完以后没有任何反应.这个时候我们应该 ...

  5. 委托学习总结(二)匿名方法和lambda表达式

    之前总结了委托这个困惑着大多初学者的概念,继续来学习匿名方法和lambda表达式 (1)我们之前写了这样一段代码 //自定义一个委托 public delegate int Expression(in ...

  6. Entity Framework工具POCO Code First Generator的使用

    在使用Entity Framework过程中,有时需要借助工具生成Code First的代码,而Entity Framework Reverse POCO Code First Generator是一 ...

  7. Azure Cosmos DB 使用费用参考

    之前在学习Cosmos DB 中SQL API(DocumentDB) 的时候,也就是之前做的一些笔记,看到有使用费用的一些介绍,就有兴趣的去了解了下,做了一下简单的总结. 想了解更多或是购买使用的还 ...

  8. [控件] BookTextView

    BookTextView 效果 说明 1. 支持富文本 2. 支持自定义view 3. 支持阅读百分比 源码 https://github.com/YouXianMing/UI-Component-C ...

  9. 如何添加设备UDID到开发者中心

    如何添加设备UDID到开发者中心 1. 登录开发者中心 2. 选择证书那一项 3. 选择Devices 4. 点选+按钮 5. 填上设备的UUID以及设备名字然后添加上 大功告成:) 附录: 如何获取 ...

  10. Office 365 Pass-through身份验证及Seamless Single Sign-On

    Hello 小伙伴们, 这篇文章将视点聚焦在传递身份验证(Pass-through Authentication)上,将分享如何安装,配置和测试Azure Active Directory(Azure ...