《Effective Modern C++》条款1

条款1:理解模板类型推导(Understand template type deduction)

模板的类型推导是现代C++最引人注目的特性之一auto的基础,因此要真正理解模板类型推导的各个方面。

函数模板:

1
2
template<typename T>
void f(ParamType param);

调用方式:

1
f(expr);  // call f with some expression

在编译过程中,编译器使用expr来推导两个类型:TParamType。被推导出的T的类型不仅取决于expr的类型,还取决于ParamType的形式。有三种情形:

  • 情况1:ParamType是一个引用或指针,但不是一个万能引用(Universal Reference)

    类型推导的工作方式如下:

    1. 忽略expr的类型的引用或指针部分(即去掉&*
    2. 然后,模式匹配expr的类型和ParamType来获得T
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template<typename T>
    void f(T& param); // param is a reference

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

    const char * ptrToName = name;
    const char name[] = "J. P. Briggs"; // 数组衰变成指针

    void someFunc(int, double); // 函数类型会衰变成函数指针


    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&

    f(name); // T is const char[13], param's type is const char (&)[13]

    f(someFunc); // T is void (*)(int, double)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<typename T>
    void f(const T& param); // param is now a ref-to-const

    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&
  • 情况2:ParamType是一个万能引用(Universal Reference)

    • 如果expr是一个左值(lvalue),TParamType都被推导成左值引用。
    • 如果expr是一个右值(rvalue),使用一般的推导规则(情况1中所示)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<typename T>
    void f(T&& param); // param is now a unicersal reference

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

    f(x); // x is lvalue, so T and param's type are both int&
    f(cx); // cs is lvalue, so T and param's type are both const int&
    f(rx); // rc is lvalue, so T and param's type as both const int&
    f(27); // 27 is rvalue, so T is int, param's type is int&&
  • 情况3:ParamType既不是指针也不是引用

    这种情况下,进行的是按值传递。按值传递会拷贝一个全新的对象,该对象会忽略expr的修饰符(constvolatile)。其推导方式如下:

    1. 忽略expr类型的引用部分
    2. 忽略expr类型的constvolatile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    template<typename T>
    void f(T param); // param is passed by value

    int x = 27;
    const int cx = x;
    const int& rx = x;
    const char* const ptr = "Fun with pointers";

    const char * ptrToName = name;
    const char name[] = "J. P. Briggs"; // 数组衰变成指针

    void someFunc(int, double); // 函数类型会衰变成函数指针

    f(x); // T is int
    f(cx); // T is int
    f(rx); // T is int
    f(ptr); // T is const char*

    f(name); // T is const char*

    f(someFunc); // T is void (&)(int, double)

要点:

  • 在模板类型推导过程中,引用实参的引用性被忽略
  • 对万能引用(universal reference)参数进行类型推导时,左值实参产生左值引用,右值实参产生右值引用
  • 对按值传递的参数进行类型推导时,忽略实参的const和/或volatile修饰符
  • 对按值传递的参数进行类型推导时,数组和函数实参将退化为指针

评论