[Cpp基础] [07] 复杂类型和类型推断

Last Updated on 2022年9月28日

数组类型和函数类型

尽管在大部分场合中,数组类型和函数类型都被转换成某种指针使用,然而它们确实是某种类型.
这一点在类型推导中很重要,因为类型推导可以推导出数组类型/函数类型

  • 数组类型逻辑上类似于std::array,由TN两个属性决定唯一的数组类型,称T[N]型对象为数组类型,其中N必须为constexpr,称T array[N]T[N]的一个实例.
    • TN必须都相同才是同一个array类型.
  • 数组类型可以被隐式decay为指向数组首元素的指针.
    • 转换为指针后,长度信息就丢失了.
    • T[N]做函数形参时,编译器一定会将形参转换为指针,从而可以接受长度不同的数组,长度信息也随之丢失.
  • 数组的长度信息未丢失时,编译器及用户是可以获取到这些长度信息的.sizeof(array)/sizeof(array[0])就是元素数量.
  • 可以使用引用型形参来保留数组的长度信息,对于模板,还可以使用自动推断.
    • 自动推断:template<class T>fun(T &);template<class T,int N>fun(T (&array)[N])
    • 手动声明:fun(int (&array)[100])
  • 由于数组是一个类型,因此尽管对于T array[N],array&array的物理地址相同,但二者的意义是不同的,前者是一个T[N]数组,可以隐式decay成一个T*,而后者则是一个指向T[N]的指针.
  • retType (arg1Type,arg2Type...)这样的类型称为函数类型.称retType fun(arg1Type,...)中的fun为该类型的实例,后文中使用using FT= retType (arg1Type,arg2Type,...)来表示函数类型.
  • 对于函数fun,fun&fun意义上是一致的,前者是一个FT型实例,会隐式decay成一个FT *,而后者就是指向FT的指针

decltype和auto

类型推断相关的内容主要有decltype和auto,其中,auto是通过初始化进行推导,其行为和模板类型推断完全一致

decltype用于获得对象的精确类型,涉及的细节较多参考Cppreference,一般来说,最好搭配std::decay使用,保证得到的类型是一个简单类型.

decltype

  • 当输入是一个对象时,推断出的类型一定和输入对象完全相同.
  • 当输入是一个左值表达式时,推断的结果是一个引用,该引用可以精确绑定到输入表达式.
  • 注意:decltype用于推断数组或函数时,不会推导出指针.对于数组decltype(array) ar;会创建一个新的数组,decltype(fun) * fp;则创建了一个函数指针.
    • 尽管decltype(fun) f2;逻辑上是创建一个"函数对象",但是由于C++中没有办法对这样创建的函数进行初始化,所以这种场景仅相当于声明函数, 也就是说: 声明了一个名为f2的函数,该函数的类型与fun一致.
  • decltype()自身就是可以作为类型使用,例如std::decay<decltype(*iter)>::type是常见的用法,它可以保证推导出一个简单类型(不含const,不含引用).

auto

auto和函数模板实参推断的行为完全一致,仅语法上形式不同,为了便于说明,本节仅用模板实参推断进行描述.

  • 当进行函数调用时,可以通过函数的实参来对模板实参进行推断,从而获得T的真实类型.
    • T && arg时,推断出的RealT一定包含引用, 且可能包含const, 会进一步按引用折叠来决定最终的类型.
    • 一般没有const T && arg这样的用法
    • T arg型形参推断的结果不含const, 推断出的数组/函数会退化为指针
    • T & arg,推断结果可以包含const,推断出的数组/函数不会退化成指针
    • const T &的推断结果肯定不含const
// 注意:直接参与推导的部分必须是`T&&`形式的才能触发引用折叠推导, 这里的`MyType<T>&& arg`并不是简单的`T&&`型, 直接参与推导的是`MyType<T>`.
// 实际的效果是: 1. 调用函数时,实参必须是MyType<T>类型的右值. 2. MyType<T> 中的T实际不需要推导,它一定和实参一致.
template<class T>
void foo2(MyType<T>&& arg){

}
  • 完美转发:将T &&std::forward配合即可, 此时推导出的T会和实参的类型完全一致, std::forward<T>(arg)的效果和cast是类似的.
template<class T>
 void Relay(T && arg){
    bar(std::forward<T>(arg));
}
  • 一般性举例
    {
    int raw;
    int any
    int &ref=raw;
    auto b=ref;// auto为int;
    auto &b1=ref;// auto为int;
    decltype(ref) c=any;// decltype(ref) 为 int& 
    }
    {
    const int raw=0;
    const int any=0;
    const int &ref=raw;
    auto b=ref;//auto为int
    auto &b1=ref;//auto为const int
    delcytple(ref) c=any;//delcytple(ref)为 const int&
    }
    int array[10]
    auto p=array;//auto为 int*
    auto &p=array;//auto为 int[10] &
    decltype(array) a2;//等价为 int a2[10];