auto类型推导

auto的作用

auto是我在编码中经常使用到的C++11新特性之一,主要用于变量的自动类型推导,如auto num=3,则推导出num的类型为int32_t

auto的优势

相较于原始的显式类型去声明变量类型,auto的优势有以下几点:

  1. auto变量一定要被初始化,可以避免变量忘记初始化

    // auto i;                 //   error: declaration of 'auto i' has no initializer
    auot i=1;                  //   ok
    
  2. 可简化变量/对象的声明

    std::vector<int32_t> vec(1,2,3,4);
    auto iter=vec.begin();     //   auto自动推导为std::vector<int32_t>::iterator
    
  3. 在某些场合无法判断出类型时,可用auto自动推导(如lambda表达式)

    auto func = [](int32_t a, int32_t b)
        {
            return a + b;
        }
    
  4. 可自适应类型,避免类型转换出现可能的类型错误

    int64_t getFileSize(const std::string& filePath)
    {
        std::fstream fs(filePath,std::ios::in|std::ios::binary);
        if (!fs.is_open())
        {
            return 0;
        }
        fs.seekg(0,std::ios::end);
        int64_t len=fs.tellg();
        fs.close();
        return len;
    }
    
    int main()
    {
        // int32_t len=getFileSize("./main.exe");            //  可能发生整形溢出
        auto len=getFileSize("./main.exe");                  //  自动推导为int64_t类型
    }
    

auto类型推断

auto类型推断的逻辑和模板类型推断是一致的,所以我们可以先了解下模板类型推断规则,然后再去推广到auto类型推断

模板类型推断,我们可以以一段伪代码为例

// 模板函数
template<typename T>
void f(ParamType param);

// 模板函数调用
f(expr);

在编译过程中编译器会使用expr推断两种类型:一个T的类型,一个是ParamType。而这两种类型往往是不一样的,因为ParamType通常会包含修饰符,比如const或者引用,所以这里模板函数推断过程可以分为以下几种情况

  • ParamType是指针或普通引用类型,模板函数如下:

    template<typename T>
    void f(T& param);
    

    推导的过程如下

    1. expr具有引用类型,先将引用部分忽略

    2. expr的类型和ParamType的类型进行匹配,来决定T的类型

      int32_t x=27;
      const int32_t cx=x;
      const int32_t& rx=x;
      f(x);    //   void f<int32_t>(int32_t &param),T的类型为int32_t,param的类型为int32_t&                 
      f(cx);   //   void f<const int32_t>(const int32_t &param),T的类型为const int32_t,param的类型为const int32_t&
      f(rx);   //   void f<const int32_t>(const int32_t &param),T的类型为const int32_t,param的类型为const int32_t&
      
  • ParamType是一个万能引用(Universal Reference),模板函数如下:

    template<typename T>
    void f(T&& param);
    

    推导的过程如下

    1. 如果expr是个左值,TParamType都会被推导为左值引用。首先,这是模板类型推断中唯一将T推断为引用的情况;其次,虽然ParamType的声明使用右值引用语法,但它最终却被推断成左值引用。

    2. 如果expr是个右值,参考ParamType是指针或者普通引用类型情况,先将引用部分忽略,然后exprParamTyoe的类型进行匹配,来决定T的类型

      int32_t x=27;
      const int32_t cx=x;
      const int32_t& rx=x;
      f(x);   //   void f<int32_t &>(int32_t &param),T的类型为int32_t&,param的类型为int32_t&       
      f(cx);  //   void f<const int32_t &>(const int32_t &param),T的类型为const int32_t&,param的类型为const int32_t&   
      f(rx);  //   void f<const int32_t &>(const int32_t &param),T的类型为const int32_t&,param的类型为const int32_t& 
      f(27);  //   void f<int>(int &&param),T的类型为int32_t,param的类型为int32_t&&
      
  • ParamType既不是指针也不是引用。这种情况也就是所谓的按值传递,模板函数如下:

    template<typename T>
    void f(T param);
    

    无论传入的是什么expr,param也只是一份副本。推导的过程如下:

    1. expr具有引用类型,则忽略其引用部分

    2. expr携带const限定符,也忽略它

      int32_t x=27;
      const int32_t cx=x;
      const int32_t& rx=x;
      f(x);   //  void f<int32_t>(int32_t param),T的类型为int32_t,param的类型为int32_t      
      f(cx);  //  void f<int32_t>(int32_t param),T的类型为int32_t,param的类型为int32_t
      f(rx);  //  void f<int32_t>(int32_t param),T的类型为int32_t,param的类型为int32_t
      f(27);  //  void f<int32_t>(int32_t param),T的类型为int32_t,param的类型为int32_t
      
  • 数组参数属于模板类型推断的一种特殊情况

    1. 按值传递时由于数组退化为指针的规则,数组参数会被当做指针参数来处理,所以数组会被推断为一个指针类型

      template<typename T>
      void f(T param)
      {
      
      }
      
      int main()
      {
          const char szName[]="Mark Lin";       // szName的类型为const char szName[9]
          f(szName);   //  void f<const char *>(const char *param),T的类型为const char*,param的类型为const char*
      }
      
    2. ParamType是指针或普通引用类型,T的类型会被推导为数组类型,ParamType则为数组引用

      template<typename T>
      void f(T& param)
      {
      
      }
      
      int main()
      {
          const char szName[]="Mark Lin";     //   szName的类型为const char szName[9]
          f(szName);  //  void f<const char [9]>(const char (&param)[9]),T的类型为const char[9],param的类型为const char (&)[9])
      }
      

      使用这种声明有一个妙用。我们可以创建一个模板来推断出数组中包含的元素数量:

      template<typename T, std::size_t N> 
      constexpr std::size_t arraySize(T (&)[N]) noexcept 
      { 
          return N; 
      } 
      

auto的类型推断机制是和模板类型推断是一致的,auto关键字扮演的是模板类型推断中T的角色,而类型说明符扮演的是ParamType的角色

  • auto修饰为指针或普通引用类型

    //类比如下模板函数
    //template<typename T>
    //void f(T& param)
    
     int32_t num=27;
     auto& x=num;   // auto被推断为int32_t,x的类型为int32_t&
     auto& lx=x;    // auto被推断为int32_t,lx的类型为int32_t&
    
  • auto修饰是一个万能引用(Universal Reference)

    //类比如下模板函数
    //template<typename T>
    //void f(T&& param)
    
    int32_t num=27;
    auto&& x=num;   // auto被推断为int32_t&,rx的类型为int32_t&
    auto&& lx=x;    // auto被推断为int32_t&,rx的类型为int32_t&
    auto&& rx=27;   // auto被推断为int32_t,rx的类型为int32_t&&
    
  • ParamType既不是指针也不是引用

    //类比如下模板函数
    //template<typename T>
    //void f(T param)
    
    auto x = 27;   // auto被推断为int32_t,x的类型为int32_t
    const auto cx = x;  // auto被推断为int32_t,cx的类型为const int32_t
    
  • 数组参数和函数指针的情况

    const char name[] =  "R. N. Briggs";
    auto arr1 = name; // auto被推断为const char*,arr1的类型为const char*
    auto& arr2 = name; // auto被推断为const char[13],arr2 的类型为const char (&)[13]
    void someFunc(int, double); 
    auto func1 = someFunc; // func1的 类型为 void (*)(int, double)
    auto& func2 = someFunc; // func2的类型为 void (&)(int, double)
    
  • 大多数场景下auto推断规则和模板参数推断规则相通,有一种特殊情况,就是统一初始化式。可以看下下述3个例子的区别

    // ok
    auto x = { 11, 23, 9 }; // x的类型为 std::initializer_list<int>
    
    // error
    template<typename T> // 和auto x等同的模板类型推断
    void f(T param); 
    
    f({ 11, 23, 9 }); // 错误!这里不能推断T的类型。
    
    // ok
    template<typename T>
    void f(std::initializer_list<T> initList);
    
    f({ 11, 23, 9 });                   // T被推导成int,initList的
                                        // 类型是std::initializer_list<int>
    

参考资料

[^1]https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值