• [Effective Modern C++] Item 1. Understand template type deduction


    条款一 了解模板类型推断

    基本情况

    首先定义函数模板和函数调用的形式如下,在编译期间,编译器推断T和ParamType的类型,两者基本不相同,因为ParamType常常包含const、引用等修饰符

    template<typename T>
    void f(ParamType param); // 函数模板形式
    f(expr); // 函数调用

     存在T的类型即为expr类型的情况,如下T为int

    templat<typename T>
    void f(const T& param);
    int x = 0;
    f(x); // T -> int

    但是T的类型的推断不仅与expr有关,还和ParamType有关。有如下三种情况:

    • ParamType是指针或引用类型,但不是通用引用(universal reference)

      如果expr是引用类型,则忽略引用部分

      然后通过模式匹配expr与ParamType来决定T

      向函数模板传递一个const对象是合法的,T会添加对应的const,如下所示

    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&

      以上示例演示的是左值引用形参,对于右值应用也一样,当然只有右值实参能传入右值引用形参,但对于类型推断没有影响

      const T&的情况

    template<typename T>
    void f(const T& param);
    
    int x = 27;
    const int cx = x;
    const int& rx = x;
    
    f(x); // T -> int, param -> int&
    f(cx); // T -> int, param -> const int&
    f(rx); // T -> int, param -> const int&

      T*的情况

    template<typename T>
    void f(T* param);
    
    int x = 27;
    const int *px = &x;
    
    f(&x); // T int, param -> int*
    f(px); // T const int, param -> const int*
    • ParamType是通用引用

      如果expr是左值,则T与ParamType被推断为左值引用。这非常不同寻常,首先这是模板类型的唯一情境T被推断为引用,然后即使ParamType的语法是一个右值引用,它推断出的类型也是左值引用

      如果expr是右值,则适用情况一的规则

    template<typename T>
    void f(T&& param);
    
    int x = 27;
    const int cx = x;
    const int& rx = x;
    
    f(x); // x -> lvalue, T -> int&, param -> int&
    f(cx); // x -> lvalue, T -> const int&, param -> const int&
    f(rx); // x -> lvalue, T -> const int&, param -> const int&
    f(27); // 27 -> rvalue, T -> int, param -> int&&

      当使用通用引用的时候,左值实参与右值实参的类型推断不同,但是对于非通用引用则没有区别

    • ParamType既不是指针又不是引用

      如果expr是引用类型,则忽略引用部分

      在忽略引用部分后,如果expr是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 -> T
    f(cx); // T -> int, param -> T
    f(rx); // T -> int, param -> T

      由于是拷贝,实参的const不再生效

      以下是一个特殊情形分析

    template<typename T>
    void f(T param);
    
    const char* const ptr = "Fun with pointers";
    
    f(ptr); // T -> const char*, param -> const char*

      ptr是按值传递,则ptr的const需要舍弃,则param的类型为const char*

    数组实参

    尽管数组类型与指针类型在有时候可以转换(许多情境下数组退化为指向第一个元素的指针,array-to-pointer decay rule),但依旧值得探讨一些细节问题。

    实际上没有数组类型的形参,其会转换为指针

    一般数组情形

    template<typename T>
    void f(T param);
    
    const char name[] = "J. P. Briggs";
    
    f(name); // T -> const char*, param -> const char*

    引用数组情形

    template<typename T>
    void f(T& param);
    
    const char name[] = "J. P. Briggs";
    
    f(name); // T -> const char[13], param -> const char(&)[13]

    以下函数在编译器能直接获取已知数组的长度

    template<typename T, std::size_t N>
    constexpr std::size_t arraySize(T (&)[N]) noexcept {
        return N;
    }

    函数实参

    函数类型也会退化为函数指针类型,其规则和数组相同

    void someFunc(int, double);
    
    template<typename T>
    void f1(T param);
    
    template<typename T>
    void f2(T& param);
    
    f1(someFunc); // T -> void (*)(int, double), param -> void (*)(int, double)
    f2(someFunc); // T -> ?, param -> void (&)(int, double)

    总结

    • 在模板类型推断过程中,引用实参被当做非引用处理,所以他们的引用性被忽略
    • 当推断通用引用的形参时,左值实参特殊对待
    • 当推断传值形参的类型时,const、volatile的实参忽略其const、volatile
    • 在模板类型推断的过程中,数组或函数的实参退化为指针,除非他们被用来初始化引用
  • 相关阅读:
    c++下使用邮槽实现进程间通信
    c++下基于windows socket的多线程服务器(基于TCP协议)
    C++实现线程同步的几种方式
    c++多线程编程:实现标准库accumulate函数的并行计算版本
    c++多线程在异常环境下的等待
    c++下基于windows socket的服务器客户端程序(基于UDP协议)
    c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)
    C++解决error C4996报错
    Python读取UTF-8编码文件并使用命令行执行时输出结果的问题
    P4655 [CEOI2017]Building Bridges 题解
  • 原文地址:https://www.cnblogs.com/Azurewing/p/4722230.html
Copyright © 2020-2023  润新知