Effective Mordern C++ 学习笔记之——类型推导

785 阅读3分钟

更多精彩文章,欢迎关注作者微信公众号:码工笔记

一、类型推导

Item 1: 模板类型推导

Case 1: 形参是引用或指针类型,但不是 Universal Reference

  1. 形参为引用
template<typename T>
void f(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); //T is int, param's type is int&
f(cx);  //T is const int; param's type is const int&
f(rx);  //T is const int; param's type is const int&

  1. 形参为 const 引用
template<typename T>
void f(const T& param); //param 指定了const,后续在类型推导时不需要将实参的const推导到T中

int x = 27;
const int cx = x;
const int& rx = x;

f(x);   //T is int, param's type is const int&
f(cx);  //T is int, param's type is const int&
f(rx);  //T is int, param's type is const int&
  1. 形参为指针
template<typename T>
void f(T *param);

int x = 27;
const int *px = &x;

f(x);   //T is int, param类型为int*
f(px);  //T is const int; param类型为const int*

注:形参为 const 指针的情况参考形参为 const 引用时,即T推导为int,params类型为const int*;

Case 2:形参为 Universal Reference

这里按实参类型分为两种情况:

  • 实参为 lvalue,则T和paramType都推导为lvalue reference。
  • 实参为 rvalue,参考 Case 1
template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);   //T为int &,param类型为 int&
f(cx);  //T为const int&, param类型为const int&
f(rx);  //T为const int&, param类型为const int&
f(27);  //T为int, param类型为int&&   【注意】

Case 3:形参既不是指针也不是引用(包括Universal Reference)

值传递:

  • 如果实参为引用类型,则只关注其类型而忽略其是一个引用
  • 忽略实参的 const 和 volatile 属性
template<typename T>
void f(T param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);   //T为int,param类型为int
f(cx);  //T为int,param类型为int
f(rx);  //T为int,param类型为int
template<typename T>
void f(T param);

const char *const ptr = "Fun with poiters";

f(ptr);   //T和param都是const char* (描述ptr变量本身的后一个const被忽略了)

另外,实参为数组或函数名的情况下,如果形参不是引用类型,则其类型会被推导为指针。

Item 2:auto 类型推导

逻辑基本与模板类型推导相同,注意以下两点:

  • auto的类型推导会将大括号初始化表达式推导为std::initializer_list,而模板类型推导不会
    • auto x = {xx, yy, ...}
  • auto作为函数返回值类型或lambda表达式参数类型时,表示使用模板推导而不是auto类型推导
    auto createInitList()
    {
      return {1,2,3};   //error: can't deduce type for {1,2,3}
    }
    
    std::vector<int> v;
    ...
    
    auto resetV = [&v](const auto& newValue) { v = newValue; }  //c++ 14
    ...
    
    resetV({1,2,3});   //error: can't deduce type for {1,2,3}
    
    

Item 3:decltype 类型推导

给定一个变量名或表达式,输出其类型。注意:

  • 如果给定的是左值表达式(类型为T)而非变量名,decltype返回 T&
  • c++14支持decltype(auto),它由其初始化值来做类型推导,使用的是decltype的规则

示例:返回数据为容器元素,其类型取决于Container,返回值类型使用decltype

//c++14
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index, i)
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}

//c++11
template<typename Container, typename Index>
auto authAndAccess(Container &&c, Index i) -> decltype(std::forward<Container>(c)[i])
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}

二、auto的使用

  • auto类型的变量必须被初始化,可以减少类型不匹配的问题,降低重构开销,减少typing
  • auto类型可能会由于"Invisible" proxy type而出现推导错误的情况
    • 可使用显式类型的初始化值来确保 auto 能正确推导出目标类型,并且能向读者传达正确的信息
1double calcEpsilon(); //返回值类型为double
//显式指定强转为float类型,显式cast可向读者传递此处的确就是要降低精度
auto ep = static_cast<float>(calcEpsilon());