c++11新特性:decltype

源从何来

很长时间以来我们都希望有这么一个功能:给定一个对象,通过某个函数或者关键字,获得它的类型。c++有typeid的实现,也就是返回一个对象的typeid字符串,但是这个字符串不能用来声明一个新变量。因此c++11就出现了decltype这个关键字。

三大作用

  • 声明返回类型

    1
    2
    template<typename T1, typename T2>
    decltype(x + y) add(T1 x, T2 y);

    还有另外一种写法:

    1
    2
    template<typename T1, typename T2>
    auto add(T1 x, T2 y) -> decltype(x + y);//和lambda有点像?

    在上面这个函数里,有两个模板参数,变量x和y对operator + 进行操作符重载之后,我们不知道函数的返回类型是什么,这个时候我们就可以用decltype来做。

  • 元编程
    在模板函数或者模板类里面,可以灵活使用decltype来获得模板类型定义新变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<typename T>
    void foo(T obj){
    map<int, string> coll;
    //通过对象名拿到类型
    decltype(coll)::value_type elem;
    //得到模板参数对象的迭代器名
    typedef typename decltype(obj)::iterator iterType;
    //定义迭代器
    iterType iter;
    }
  • 传递lambda表达式的类型
    使用decltype和lambda可以起到仿函数的作用。

    1
    2
    3
    4
    auto cmp = [](const Foo& f1, const Foo& f2){
    return f1.data() > f2.data();
    };
    std::set<Foo,decltype(cmp)> coll(cmp);

和auto的区别

  • auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,但是不实际计算表达式的值。
  • 编译器推断出来的auto类型有时候和初始值的类型并不完全一致,编译器会适当的改变结果类型使其更符合初始化规则,例如,auto一般会忽略掉顶层const,而把底层const保留下来,与之相反,decltype会保留变量的顶层const。
  • decltype的结果类型与表达形式密切相关。有一种情况需要特别注意:对于decltype 所用表达式来说,如果变量名加上一对括号,则得到的类型与不加上括号的时候可能不同。如果decltype使用的是一个不加括号的变量,那么得到的结果就是这个变量的类型。但是如果给这个变量加上一个或多层括号,那么编译器会把这个变量当作一个表达式看待,变量是一个可以作为左值的特殊表达式,所以这样的decltype就会返回引用类型。