zoukankan      html  css  js  c++  java
  • C++11初探:类型推导,auto和decltype

    类型推导可以说是C++模拟动态语言特性的起点,就从这里开始这个系列吧。

    auto

    使用迭代器的时候,类型总是一件烦心的事。

    vector<vector<int> > v;
    vector<vector<int> >::iterator it = v.begin(); 

    函数指针也同样, 类型声明很蛋疼:

    int add(int x,int y){
        return x+y;
    }
    
    int main(){
        int (*func)(int,int) = add;
        cout<<func(1,2)<<endl;
    }

    我既然把v.begin()赋给it, 类型已经在编译期确定了,编译器知道正确的类型是什么,再加一个类型声明实在很繁琐。C++11 有了auto。我们可以这样写:

    vector<vector<int>> v; // C++11 可以不用在'>>'之间加空格了!
    auto it = v.begin(); auto func = add;

    编译器会根据值的类型,推导出autob变量。类型的推导是在编译期就完成的,仍是静态类型,和脚本语言不同。实际上是一个语法糖。但由于C++对模板的大量使用,一个变量的类型有时过于复杂难以写出,这样的语法糖是必要的。

    很简单,不是么?(实际会有一些细节问题,文末再说)

    decltype

    如果我们只是想用一个编译器推导的类型声明一个变量,但不初始化,auto无能为力。此时可以用decltype。

    int add(int x,int y){
        return x+y;
    }
    int main(){
        double i=0;
        decltype(i) a; // double
        decltype(add()) b; //int 注意括号。不带括号就是函数指针了。
    }

    需要说明,decltype仍然是编译期行为,add()函数不会真正执行,只是利用它推导了类型。

    返回类型后置

    考虑一个模板函数

    template<typename U, typename V>
    ??? foo(U u, V v){
        ...
        return u*v;
    }

    总之我要在这个函数做点事情,最后返回一个u*v类型的东西。返回类型该怎么写?

    有了decltype,似乎可以这么:

    template<typename U, typename V>
    decltype(u*v) foo(U u, V v){ //WRONG!
        return u*v;
    }

    但是不行!u,v不在作用域。

    u,v不能用,自己搞出一个可以用的U,V类型的变量总行了吧XD

    template<typename U, typename V>
    decltype(U(0)*V(0)) foo(U u, V v){
        return u*v;
    }

    试一下,foo(2,3.3)返回6.6,貌似正常。但是!你怎么知道U,V可以接受一个0作为构造参数的?U,V不一定是数值,有可能是重载了'*'操作符的奇怪东西。没关系,指针总是可以这样初始化的:

    template<typename U, typename V>
    decltype(*(U*)(0)**(V*)(0)) foo(U u, V v){
        return u*v;
    }

    但是你不觉得太丑了么。。。

    C++11引入了返回类型后置解决这个问题,

    template<typename U, typename V>
    auto foo(U u, V v) -> decltype(u*v){
        return u*v;
    }

    语法很清晰。总之就是,返回类型后置以后,就进入函数的作用域了,参数都能用了!

    另外还有一个用处,假设你要写个类

    //A.h
    struct A {
        struct B {};
        B func();
    };
    
    //A.cpp
    A::B A::func(){
        return B();
    }

    那个A::B怎么看都别扭,用返回类型后置,可以这样:

    auto A::func() -> B {
        return B();
    }

    再来一次,后置的返回类型,处在函数作用域里,B已经可见,不需要A::修饰了。

    但你肯定有一个问题,既然我函数代码已经给出了,编译器完全可以推断出返回类型是什么,为什么还要自己显式给出返回类型?如果能这样就好了:

    template<typename U, typename V>
    auto foo(U u, V v){
        return u*v;
    }

    但是很遗憾,不行。由于“复杂性”,标准没有这样做。这也容易理解,假如你的程序编译成了库,只给了别人头文件里的函数签名,函数体对编译器是不可见的,那你这个auto算什么意思?

    但事情也不那么绝对,通过匿名函数返回值类型可省略这个特性加上auto,有些情况下我们的确可以不用写返回类型。以后再谈。

    一些细节

    虽然我很不想写细节,但C++的东西总不是那么简单,为了完整性,把这些黑暗角落放到最后,希望不会影响对以上特性的好感...

    • auto+引用
    int i = 0;
    int& r = i;
    auto a = r; // type of a?
    a = 10;
    cout<<i<<endl; // value of i?

    直观上感觉,r是i的引用,a自然也会推导出int&这个类型。但实际上,r只是i的别名,a类型的推导结果还是int。于是,对a的修改和i无关,结果输出0。如果想推导出引用,需要这样

    auto& a = r; // auto& a = i也可以
    • auto+const

    还有一点,auto会忽略顶层的const。

    const int i = 0;
    auto a = i; // a: int
    audo b = &i // b: const int*

    其中,a是普通的int, 因为i是const int,auto把顶层的const忽略了。b是const int*, 这个可以这样看,const int*实际上是“(const int)*”(当然代码不能这样写),const不是顶层修饰符了,就没有忽略。

    如果想得到推导出一个const类型,需要

    const auto a = i; // a: const int

    另外,decltype和auto不同,它不会忽略引用和顶层const修饰。

    const int i=0;
    int j=0, &r = j;
    decltype(i) a; //const int
    decltype(r) b; //int& 

    最后,比较特别的是,如果decltype里面的表达式被包含在括号中,视为对表达式求值的类型。

    decltype((i)) a;//error: a is int& and must be initialized

    对i求值返回的是int&。

    目录

  • 相关阅读:
    手机号码正则表达式
    POJ 3233 Matrix Power Series 矩阵快速幂
    UVA 11468
    UVA 1449
    HDU 2896 病毒侵袭 AC自动机
    HDU 3065 病毒侵袭持续中 AC自动机
    HDU 2222 Keywords Search AC自动机
    POJ 3461 Oulipo KMP模板题
    POJ 1226 Substrings KMP
    UVA 1455 Kingdom 线段树+并查集
  • 原文地址:https://www.cnblogs.com/npbool/p/3433360.html
Copyright © 2011-2022 走看看