1. 类型推导

38 阅读4分钟

用函数传参来想下面规则,会容易很多

1. 理解模板型别推导

template<class T>
void func(ParamType param); // 这里ParamType可能是
                            //const、指针、引用修饰的类型T
func(expr); // 通过表达式调用

T的推导不仅与expr有关,还与ParamType有关

  1. expr(实参)的影响
  • 有引用的实参会被视为无引用,如int& x = 1;func(x);T推导出来类型就是int,而非int &
  1. ParamType(形参)也会影响T的推导,先决定形参类型,再推导出T,分为:
  • 引用或指针&、*,形参会保留const修饰,因为底层const指针只能被底层const指针接受
  • 万能引用&&,左值会被推导为左值引用(唯一T为引用的情形),如int x = 1;func(x);//这里T推导出来就是int &,而右值则会去掉引用,T为常规的,如func(27);//T推导出来就是int
  • 值传递,会去掉修饰变量的const属性,如const char* const==>const char*
  1. 两者共同作用
    1. 如果实参是数组
    • 值传递会退化为指针类型,如char a[10]==>char *
    • 引用传递,则会保留数组大小,如const char (&)[10]
    //在编译期间返回一个数组大小的常量值(//数组形参没有名字,
    //因为我们只关心数组的大小)
    template<typename T, std::size_t N>
    constexpr std::size_t arraySize(T (&)[N]) noexcept
    { 
        return N; 
    } 
    
    1. 如果实参是函数
    • 值传递还是会退化为指针int func(char c)==>int (*)(char)
    • 引用传递,int func(char c)==>int (&)(char)
    template<class ReturnT,class Param1>
    void getFuncType(ReturnT (*)(Param1)){
        ...;
    }
    

2. 理解auto型别推导

  1. 一般情况下,auto类型推导和template类型推导一样,可以认为auto是template的语法糖
  • auto ==> T
  • 对auto的修饰 ==> ParamType
int x = 1;

const auto& rx = x; // auto推导为int
//===等价于===
template<class T>
void fun(const T& rx);
func(x); //T推导为int
  1. 两者唯一区别在于,auto会假定大括号括起来的初始化表达式代表一个std::initializer_list,而template却不会,直接报错
auto x = { 11, 23, 9 };         //x的类型是std::initializer_list<int>

template<typename T>            //带有与x的声明等价的
void f(T param);                //形参声明的模板

f({ 11, 23, 9 });               //错误!不能推导出T
  1. cpp14中,auto推导返回值类型、lambda中auto修饰形参时使用的template推导,所以不能推导{}初始化表达式

这里cpp11不允许auto推导返回值类型,且auto只能推导单条语句的lambda表达式,如auto lambda = [](int x) { return x * 1.5; };

auto createInitList()
{
    return { 1, 2, 3 };  //错误!不能推导{ 1, 2, 3 }的类型
}

auto resetV = 
    [](const auto& newValue){ ... }; //C++14
resetV({ 1, 2, 3 });  //错误!不能推导{ 1, 2, 3 }的类型

3. 理解decltype

  1. decltype只是简单的返回名字或者表达式的类型,如
bool f(const Widget& w);        
//decltype(w)是const Widget&
//decltype(f)是bool(const Widget&)
  1. 在 C++11 中,auto + 尾置返回类型-> decltype(...))的主要用途是 解决函数返回值依赖参数类型的场景,尤其是模板函数中返回值与参数类型相关的情况。
template<typename Container, typename Index> 
auto authAndAccess(Container& c, Index i) //这里auto不是用来推导,只是暗示使用了C++11的**尾置返回类型**语法
    ->decltype(c[i]) //尾置返回类型,与参数相关
{
    return c[i];
}
  1. 在cpp14中,auto可以直接用来推导返回值类型,但是注意auto会忽视掉实参的引用,所以上面代码去掉decltype,返回的就是c[i]的临时对象,而如果是使用auto&,则一直都是返回引用;
  2. 需要真正做到按照值返回,可以使用decltype(auto)(cpp11不支持),这里是提示auto使用decltype的推导方式,也就完美做到按照返回值类型来返回
template<typename Containter, typename Index>   //仍然需要改进
decltype(auto) authAndAccess(Container& c, Index i);
  1. 但是现在并不支持右值传递,所以引入完美引用
template<typename Container, typename Index>    //最终的C++11版本
auto
authAndAccess(Container&& c, Index i)
->decltype(std::forward<Container>(c)[i])
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}
  1. decltype应用于变量名会产生该变量名的声明类型。比单纯的变量名更复杂的左值表达式decltype可以确保报告的类型始终是左值引用,如decltype(x)int,那decltype((x))int&
decltype(auto) f2()
{
    int x = 0;
    return (x);    //decltype((x))是int&,所以f2返回int&
}

4. 掌握查看型别推导结果的方法

  • 类型推断可以从IDE看出,从编译器报错看出,从Boost TypeIndex库的使用看出
  • 这些工具可能既不准确也无帮助,所以理解C++类型推导规则才是最重要的