C++类型推断和类型推导规则

C++类型推断和类型推导规则

类型推断

auto

auto只能初始化用,用于自动推断类型,编译期开始.

注意,auto只是推断值类型,const,引用这种修饰是直接去掉的.当然也可以搭配这些关键字

从 C++ 14 起,auto 能用于 lambda 表达式中的函数传参,而 C++ 20 起该功能推广到了一般的函数和模板返回值。考虑下面的例子:

auto add14 = [](auto x, auto y) -> int {
    return x+y;
}

auto add20(auto x, auto y) {
    return x+y;
//编译器自动生成一堆模板.    
auto i = 5; // type int
auto j = 6; // type int
std::cout << add14(i, j) << std::endl;
std::cout << add20(i, j) << std::endl;

[!WARNING]

auto 还不能用于推导数组类型:

auto auto_arr2[10] = {arr}; // 错误, 无法推导数组元素类型

2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
auto auto_arr2[10] = {arr};

也不允许在生成模板时直接使用,比如vector<auto>

decltype

decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。用于处理表达式.

所以decltype可以推断const,引用等值类别,会严格保留相应表达式类别,而不只是类型.

比较常见用法就是:

int a = 10;
double b = 100.1;
decltype (a+b) c = a + b;
//decltype可以推导表达式结果的类型,虽然这里用auto也可以啦.

C++11 还引入了一个叫做尾返回类型(trailing return type),利用 auto 关键字将返回类型后置来推断模板返回值类型:

template<typename T, typename U>
auto add2(T x, U y) -> decltype(x+y){
    return x + y;
}

令人欣慰的是从 C++14 开始是可以直接让普通函数具备返回值推导,因此下面的写法变得合法:

template<typename T, typename U>
auto add3(T x, U y){
    return x + y;
}

decltype(auto)

decltype(auto) 会像 decltype 一样分析表达式的类型和值类别,但语法上像 auto 一样不需要显式指定表达式。

类型推导规则

实际上类型推导规则是比较符合人的第一感觉的,这也正是它追求的目标,所以不用特意记,了解一下即可.

模板类型推断

auto的类型推断实际上基于模板,所以我们来看看模板的规则

假设一个基本情况:

template<Typename T>
f(ParamT param);

f(expr);

ParamTT本身的推断是不同的,原因很简单,ParamT可以带上各种修饰符号,比如指针,比如const.

这里有三种情况:

  • ParamType是一个指针或引用,但不是通用引用
  • ParamType是一个通用引用
  • ParamType既不是指针也不是引用

第一种情况

expr如果是引用,忽略引用部分,然后与ParamT进行模式匹配决定T

举个例子,如果这是我们的模板,

template<typename T>
void f(T& param);               //param是一个引用

我们声明这些变量,

int x=27;                       //x是int
const int cx=x;                 //cx是const int
const int& rx=x;                //rx是指向作为const int的x的引用

在不同的调用中,对paramT推导的类型会是这样:

f(x);                           //T是int,param的类型是int&
f(cx);                          //T是const int,param的类型是const int&
f(rx);                          //T是const int,param的类型是const int&

第二种模式

  • 如果expr是左值,TParamType都会被推导为左值引用。这非常不寻常,第一,这是模板类型推导中唯一一种T被推导为引用的情况。第二,虽然ParamType被声明为右值引用类型,但是最后推导的结果是左值引用。
  • 如果expr是右值,就使用正常的(也就是情景一)推导规则

举个例子:

template<typename T>
void f(T&& param);              //param现在是一个通用引用类型,带有推导的&&的都是通用引用.
        
int x=27;                       //如之前一样
const int cx=x;                 //如之前一样
const int & rx=cx;              //如之前一样

f(x);                           //x是左值,所以T是int&,
                                //param类型也是int&

f(cx);                          //cx是左值,所以T是const int&,
                                //param类型也是const int&

f(rx);                          //rx是左值,所以T是const int&,
                                //param类型也是const int&

f(27);                          //27是右值,所以T是int,
                                //param类型就是int&&

通用引用的情况就是如果左值传递就是左值引用,如果右值传递就是右值引用.

其实这里的T带有&,可以直接带入在paramT中,去掉重复(引用折叠/坍缩规则)就可以发现就是paramT

第三种情况

这意味着无论传递什么param都会成为它的一份拷贝——一个完整的新对象。事实上param成为一个新对象这一行为会影响T如何从expr中推导出结果。

  • 和之前一样,如果expr的类型是一个引用,忽略这个引用部分
  • 如果忽略expr的引用性(reference-ness)之后,expr是一个const,那就再忽略const。如果它是volatile,也忽略volatile
template<typename T>
void f(T param);                //以传值的方式处理param

int x=27;                       //如之前一样
const int cx=x;                 //如之前一样
const int & rx=cx;              //如之前一样

f(x);                           //T和param的类型都是int
f(cx);                          //T和param的类型都是int
f(rx);                          //T和param的类型都是int

因为这是一个拷贝.

特殊情况

如果数组是采取第一种情况传递进去的,会保留数组类型,不会退化,保留程度,于是我们可以写出这个:

//在编译期间返回一个数组大小的常量值(//数组形参没有名字,
//因为我们只关心数组的大小)
template<typename T, std::size_t N>                     
constexpr std::size_t arraySize(T (&)[N]) noexcept      
{                                                       
    return N;                                           
}                                                       

虽然没啥用就是了.

auto类型推断

与模板差不多,auto类型推导和模板类型推导有一个直接的映射关系。它们之间可以通过一个非常规范非常系统化的转换流程来转换彼此。

template<typename T>
void f(ParmaType param);
f(expr);                        //使用一些表达式调用f 

//当一个变量使用auto进行声明时,auto扮演了模板中T的角色,变量的类型说明符扮演了ParamType的角色。

只有一个例外,那就是初始化列表.

如何查看类型推断

用你的lsp,我的伙计.

posted @ 2025-08-30 13:46  T0fV404  阅读(7)  评论(0)    收藏  举报