• 第1课 类型推导(1)_auto关键字


    1.  auto关键字

    (1)auto的作用是让编译器自动推断变量的类型,而不需要显式指定类型。这种隐式类型的推导发生在编译期

    (2)auto并不能代表实际的类型声明,只是一个类型声明的“占位符”

    (3)auto声明的变量必须马上初始化,以让编译器推断出它的实际类型。

    【编程实验】auto的基本用法

    //1.1.cpp

    #include <iostream>
    #include <typeinfo>
    
    //编译:vc: cl 1.1.cpp
    //      g++: g++ -std=c++11 1.1.cpp
    
    using namespace std;
    
    double foo(){return 0.0;};
    
    struct m{
        int i;
    }sm;
    
    int main()
    {    
        auto x = 1;      //x:int(其实1为const int类型,auto丢弃const)
        auto y = foo();  //y:double
        auto z = sm;    //m1: struct m
        
        const auto* u = &x, v = 6; //u: const int*, v: int类型(注意不是const int)
                                   //auto的类型由u的类型决定,因此v不能初始化为6.0之类
        static auto w = 0.0;       //w:double;
        
        //auto s;   //error: 无法推导。s必须被初始化
        
        cout << "x: " << typeid(x).name() << endl;
        cout << "y: " << typeid(y).name() << endl;
        cout << "z: " << typeid(z).name() << endl;
        cout << "u: " << typeid(u).name() << endl;
        cout << "v: " << typeid(v).name() << endl;
        cout << "w: " << typeid(w).name() << endl;
        
        return 0;
    }
    /*输出结果:
    x: int
    y: double
    z: struct m
    u: int const *
    v: int
    w: double
    */

    2. auto的推导规则

    (1)当直接使用auto声明变量时(如auto varName),auto的推导结果和初始化表达式抛弃引用cv限定符(const和volatile)后的类型一致。即auto将丢弃初始化表达式的cv属性。(原因 :auto varName说明varName是按值传递,它是原对象/变量的副本,所以可以在副本上进行修改操作,所以并不需要保留const等属性)

    (2)当auto后面显式添加&时(如auto& varName)时,推导结果才会保留初始化表达式的cv属性和引用。(原因:由于引用代表对象本身,所以原对象/变量如果有cv属性,则应该被保留下来)

    (3)当auto*或auto推导结果为指针,保留指针的底层cv操作符。除2、3两种情况外,均不保留cv和引用。

    (4)同一赋值语句中,用auto来声明多个变量类型时,只有第一个变量用于auto的类型推导,然后推导出来的数据类型作用于其他变量。因此,这些变量的类型必须相同。否则,编译器则会报错。

    (5)auto是从左向右推导,类似于字面替换的方式进行。

    (6)new和初始化列表中也可以使用auto关键字

    【实例分析】auto和auto&(auto*)的差异

    //1.2.cpp

    #include <iostream>
    #include <typeinfo>
    using namespace std;
    
    //编译:vc: cl 1.2.cpp
    //      g++: g++ -std=c++11 1.2.cpp
    
    double foo(){return 0.0;};
    
    int main()
    {
        auto x  = 1;      //x: int(注意1为const int类型,但auto会丢弃const限定符)
        int* y=&x;
        
        auto* a = &x;     //a: int*  auto: int
        auto  b = &x;     //b: int*  auto: int*
        auto& c = x;      //c: int&, auto: int
        
        //auto和auto&:当初始化表达式为引用时
        auto  d = c;      //d: int,  auto: int(注意:c为引用,但会被auto会丢弃,故d为int)
        auto& e = c;      //e: int&, auto: int(auto&会保留引用和cv限制符)
        
        //auto和auto&: 当初始化表达式带cv限定符时
        const auto f = x;  //f: const int, auto: int
        auto g = f;        //注意此处:g: int(因为auto会丢弃初始化表达式的cv限定符)
        
        auto& h = f;       //注意此处:h:const int&(auto&会保留f的const属性)
        auto* i = &f;       //注意此处:i:const int*(auto*会保留f的const属性)
        
        //auto从左向右推导
        auto o = 1, &p = o, *q = &p; //o: int, p: int&, q: int*    
        auto r = 1, s = 2;           //auto根据r推导出auto为int,因此s为int
        
        //单行声明变量的陷阱(建议:多变量尽量分多行声明)
        const auto* m = &x, n = 1;  //m: const int*,但n为int(注意不带const)
        
        //初始化列表中使用auto
        auto x1(1);   //x1: int
        auto x2 {1};  //x2: int
        
        //new中使用auto
        auto x3 = new auto(1);  
        
        cout << "a: " << typeid(a).name() << endl;
        cout << "b: " << typeid(b).name() << endl;
        cout << "c: " << typeid(c).name() << endl;
        cout << "d: " << typeid(d).name() << endl;
        cout << "e: " << typeid(e).name() << endl;
        cout << "f: " << typeid(f).name() << endl;
        cout << "g: " << typeid(g).name() << endl;
        cout << "h: " << typeid(h).name() << endl;
        cout << "i: " << typeid(i).name() << endl; 
        cout << "o: " << typeid(o).name() << endl;
        cout << "p: " << typeid(p).name() << endl;
        cout << "q: " << typeid(q).name() << endl; 
        cout << "r: " << typeid(r).name() << endl;
        cout << "m: " << typeid(m).name() << endl; 
        cout << "n: " << typeid(n).name() << endl;
        cout << "x1: " << typeid(x1).name() << endl;
        cout << "x2: " << typeid(x2).name() << endl; 
        cout << "x3: " << typeid(x3).name() << endl;     
        
        return 0;
    }
    /*输出结果
    a: int *
    b: int *
    c: int
    d: int
    e: int
    f: int
    g: int
    h: int
    i: int const *
    o: int
    p: int
    q: int *
    r: int
    m: int const *
    n: int
    x1: int
    x2: int
    x3: int *
    */

    3. auto的限制

    (1)auto不能用于函数参数。如果需要泛型参数,可借助于模板

    (2)auto不能用于非静态成员变量

    (3)auto无法定义数组

    (4)auto无法推导模板参数

    【实例分析】auto使用受限

    //1.3.cpp

    #include <iostream>
    #include <vector>
    #include <typeinfo>
    using namespace std;
    
    //编译:vc: cl 1.3.cpp
    //      g++: g++ -std=c++11 1.3.cpp
    
    void func(auto a = 1){}    //error: 不能用于函数参数。泛型参数可借助模板来实现
    
    struct Foo
    {
        static const auto var2 = 0;  //OK: 静态成员函数,可用auto推导
        
        auto var1 = 0;  //error:非静态成员函数,不能用auto推导
    };
    
    int main()
    {
        char x[3] ={0};
        auto y = x;    //OK: y: int*(注意:不是char[3])
        auto z[3] = x; //error: auto无法定义数组
        
        vector<auto> v = {1}; //error: auto不能用于模板参数
        
        return 0;
    }

    4. auto的优势

    (1)auto最大优势简化代码,特别是当声明的变量类型比较复杂的时候。

    (2)避免类型声明时的错误。C/C++存在很多隐式或用户自定义的类型转换规则,这些规则不容易记忆,这时auto就可以用于自动推导。

    (3)auto的“自适应”能够在一定程序上支持泛型编程。如strlen函数返回值,在32位编译环境下,返回一个4字节的整型,64位返回一个8字节的整型。可以使用auto关键字达到代码跨平台的效果。

    【实例分析】auto的优势

    //1.4.cpp

    #include <iostream>
    #include <map>
    
    //编译:vc: cl 1.4.cpp
    //      g++: g++ -std=c++11 1.4.cpp
    
    //1. 简化迭代器变量的声明
    void func()
    {
        std::map<double, double> resMap;
        //优化前
        // std::map<double, double>::iterator = it = resMap.begin();
        // for(; it != resMap.end(); ++it){
        //     //do something;
        // }
        
        //优化后
        for(auto it = resMap.begin(); it != resMap.end(); ++it){
            //do something
        }
    }
    
    //2. 避免类型声明错误
    class PI
    {
        const float val = 3.1415927f;
    public:
        double operator* (float v){
            return (double)val * v; //精度被扩展
        }
    };
    
    //3. auto对类型的自适应
    template <typename T1, typename T2>
    double Sum(T1& t1, T2& t2)
    {
        auto s = t1 + t2; //s的类型会在模板实例化时被推导出来
    }
    
    int main()
    {
        //demo: 避免auto类型声明
        float radius = 1.7e10;
        PI pi;
        
        //operator*的返回值为double,但使用PI这个类的人可能不知道
        //PI类的设计者为了避免数据上溢而返回了double类型。这时可以
        //用auto来自动推导operator*的返回类型。同时假如PI的作者改动
        //了PI的定义,将operator*的返回值改为了long double,此时main
        //函数并不需要修改,因为auto会“自适应”新的类型
        auto circumference = 2 * (pi * radius);
        
        //demo: auto的自适应
        int a = 3;
        long b = 5;
        float c = 1.0f, d = 2.3f;
        auto e = Sum<int, long>(a, b);     //s==> long;
        auto f = Sum<float, float>(c, d);  //s==>float;
        
        return 0;
    }
  • 相关阅读:
    Linux进程间通信—消息队列
    Linux进程间通信—信号
    Linux进程间通信—信号量
    Linux进程间通信—管道
    Linux进程间通信:管道,信号量,消息队列,信号,共享内存,套接字
    安全文件传输系统
    嵌入式mp3播放器
    用C语言实现面向对象的开发
    Haskell 差点儿无痛苦上手指南
    Oracle EBS 入门
  • 原文地址:https://www.cnblogs.com/5iedu/p/6893725.html
Copyright © 2020-2023  润新知