自动类型推导

文章参考:爱编程的大丙 (subingwen.cn)

1. auto

在C++11之前,auto和static相对应用于表示变量是自动存储的,但是非static局部变量默认都是自动存储的,auto因此显得额外鸡肋。C++11中对auto进行了扩展,使他能够自动推导变量的实际类型。

1.1 推导规则

C++中auto并不是一种数据类型,而是一种用于声明类型的占位符。在使用auto时,必须对变量进行初始化,这样编译器才能推导出其实际数据类型,从而在编译时将auto替换为真正的数据类型。

auto简单使用:

auto a = 1;
auto b = 3.14;
auto c = 'a';
// auto d;		这里会报错,因为d没有经过初始化,无法使用auto

与指针、引用结合:

当auto和指针、引用结合时,可以带上const、volatile关键字,其推导规则如下:

  • 当变量是指针或引用类型时,推到结果才会带上const、volatile关键字。否则将会省略。

实例如下:

int temp = 1;
auto *a = &temp;
auto b = &temp;
auto &c = temp;
auto d = temp;
  • a类型为int *,因此auto的推导结果为int
  • b类型为int *,因此auto的推导结果为int *
  • c类型为int &,因此auto的推导结果为int
  • d类型为int,因此auto的推导结果为int

当带上const关键字时:

int tmp = 250;
const auto a = tmp;
auto b = a;
const auto &c = tmp;
auto &d = c;
  • a类型为const int,因此auto的推导结果为 int
  • b类型为int,因为b并没有声明为指针或是引用类型,因此const关键字被删去,auto的推导结果为 int
  • c类型为const int&,因此auto的推导结果为 int
  • d类型为const int&,d被声明为引用类型,const关键字得以保留,因此auto的推导结果为 int

1.2 auto限制

在一些场合中,auto关键字无法使用。

  • 不能作为函数参数使用。因为只有在函数调用时才会给到函数实参(程序运行时),而auto要求必须为变量初始化(编译时)。

  • 不能用于类的非静态成员初始化:

    class Test{
    private:
        // auto int a = 0;		编译报错。
        // static auto int b = 0;	编译报错,类的静态非常量成员必须在类外进行初始化
        const static auto int c = 0;	// 编译通过
    }
    
  • 不能使用auto关键字定义数组。

  • 不能使用auto推导模板参数:

    template <typename T>
    class Test{}
    
    int main(void){
        Test<int> t1;
        // Test<auto> t2 = t1;		错误,无法使用auto推导模板参数
        return 0;
    }
    

1.3 auto的应用

用于stl容器的遍历

  • C++98中:

    #include <map>
    #include <string>
    int main(void){
        map<int,string> person;
        map<int,string>::iterator it = person.begin();
        for(; it != person.end(); ++it){
            ...
        }
        return 0;
    }
    
  • C++11中,使用auto:

    #include <map>
    #include <string>
    int main(void){
        map<int,string> person;
        for(auto it = person.begin(); it != person.end(); ++it){
            ...
        }
        return 0;
    }
    

用于泛型编程:在使用模板时,有时并不知道变量应当定义什么类型,这时就可以使用auto:

#include <iostream>
#include <string>
using namespace std;

class A{
public:
    static int show(){
        return 1;
    }
};

class B{
public:
    static string show(){
        return "aaa";
    }
};

template <class T>
void func(){
    auto result = T::show();
    cout << result << endl;
}

int main(void){
    func<A>();
    return 0;
}

2. decltype

在某些情况下,我们希望通过表达式来获取某种类型,从而对变量进行定义,这时就可以使用decltype(是declare type的缩写)。

Eg:

int a = 10;
decltype(a) b = 100;
decltype(a+1.1) c = 3.14;
  • b被推导为int类型。
  • c被推导为double类型。

decltype的优势在于它能够在编译时推导复杂的表达式类型,而auto只能推导已初始化的变量类型。

2.1 推导规则

delctype的推导结果分情况而定:

  • 表达式是普通变量/普通表达式/类表达式,推导出的类型和表达式的类型一致。

  • 表达式是函数调用,使用decltypoe推导出的类型和函数返回值一致。

    class Test{...};
    //函数声明
    int func_int();                 // 返回值为 int
    int& func_int_r();              // 返回值为 int&
    int&& func_int_rr();            // 返回值为 int&&
    
    const int func_cint();          // 返回值为 const int。返回了一个右值
    const int& func_cint_r();       // 返回值为 const int&
    const int&& func_cint_rr();     // 返回值为 const int&&
    
    const Test func_ctest();        // 返回值为 const Test。返回的是一个右值,但是是一个对象
    
    //decltype类型推导
    int n = 100;
    decltype(func_int()) a = 0;		
    decltype(func_int_r()) b = n;	
    decltype(func_int_rr()) c = 0;	
    decltype(func_cint())  d = 0;	
    decltype(func_cint_r())  e = n;	
    decltype(func_cint_rr()) f = 0;	
    decltype(func_ctest()) g = Test();	
    
    • 变量a被推导为 int类型
    • 变量b被推导为 int&类型
    • 变量c被推导为 int&&类型
    • 变量d被推导为 int类型:因为func_cint()返回了一个右值,而对于右值而言,只有类才能携带const、volatile类型,因此需要忽略这两个限定符。
    • 变量e被推导为 const int &类型
    • 变量f被推导为 const int &&类型
    • 变量g被推导为 const Test类型。虽然func_ctest()返回了一个右值,但因为这个右值是一个对象,因此可以携带const、volatile类型。
  • 表达式不是一个变量并且是一个左值,或者表达式被()包围,那么使用decltype推导出来的是表达式类型的引用(如果有const、volatile不能忽略):

    class Test{
    public:
        int a;
    };
    
    int main(void){
        int x = 10, y = 20;
        decltype(x) a = 10;			
        decltype(x+y) b = 20;
        decltype(x = x + y) c = x;
    	
        const Test t;
        decltype(t.a) d = 10;
        decltype((t.a)) e = x;
        return 0;
    }
    
    • a:普通变量,推导出int类型。
    • b:普通表达式,推导出int类型
    • c:表达式,但最终返回左值,因此推导出int &类型。
    • d:普通变量,推导出int类型。
    • e:表达式被()包围,因此推导出cosnt int &类型。

3. 返回类型后置

引入:

在泛型编程中,有时会遇到一种情况:返回值的类型由函数的参数决定。例如:

template <typename R, typename X, typename Y>
R add(X x, Y y){
    return x + y;
}

上述代码可以,如果我们想通过decltype进行优化:

template <typename X, typename Y>
decltype(x+y) add(X x, Y y){
    return x + y;
}

这样会报错,因为delctype在编译阶段就要得出推导结果,但模板函数的参数类型却是在运行时才确定的,自然无法通过编译。为了解决这一问题,C++11引入了返回类型后置。

概述:

所谓返回后置类型,实际上就是将decltypeauto结合起来完成类型的推导。语法格式如下:

auto func(参数1, 参数2, ...) -> delctype(参数表达式)

通过上述语法,auto会自动追踪delctyple推导出的类型。所以函数可以修改为:

template <typename X, typename Y>
auto add(X x, Y y) -> decltype(x+y){
    return x + y;
}
posted @   BinaryPrinter  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示