美文网首页
23章 模板

23章 模板

作者: my_passion | 来源:发表于2022-06-26 12:27 被阅读0次

    Note

        (1) 哲学: 模板 应该从>=1个 具体实例 `泛化` 而得 
        
        (2) 支持 `类型/值` 作 `参数` 的 程序设计: 直接支持 GP
        
        (3) 优势 
        
            实例化时 获得 `模板 + 模板实参` 信息 -> `灵活性 & 运行时性能(无与伦比)` 
            
            1) `时/空效率 不逊            : 相比手工 code 
    
            2) `参数类型 不必显式相关`    : 相比 类层次
    
            3) 类型安全 
            
        (4) 代价: (编译器) `类型检查 复杂` 
    
        (5) 最大弱点: `无法直接表达` 对 `模板实参 的 要求`
        
            `概念 Concept: 表达 对 模板参数的要求` 
                            
            (视为) 谓词: 接受 `类型参数`, 返回 true / false 
                                
                "C 是容器 吗?"
                    Container<Vector<int> >() / Container<int>() 应该返回 true / false
        
        (6) 简单操作 < [] 等 -> 生成 单个机器指令 -> `模板 + inline -> 大幅缩减代码量 + 提高运行速度` 
        
        (7) 类型 别名: 短小 / 封装
            using string = std::basic_string<char> 
    

    23.1 简单字符串 模板

    1 模板 定义

        (1) 带限定符 的 模板名 的 scope 内, `模板名后的限定符` 多余  
                |          |                        |           |
                |/         |/                       |/          |/
               <C>       String<C>                  <C>     String<C>:: 后 用 String 即可
               
        (2) 模板类 `外部定义 成员: 必须 显示声明为模板`
                
            template<typename C>
            String<C>::String()
                : sz{0}, ptr {ch}
            {
                ch[0] = 0;
            }
    

    2 模板 实例化: 模板 + 模板实参列表 ( -> compiler ) -> 生成 类或函数的 过程

    (1) 实例化时机: 真正编译 前

    (2) 特例化: 实例化 的 结果 -> 特例化版本 
    

    23.2 类型检查

    1 类型等价: 模板 + 相同 模板实参 -> 相同 类型

        相同的含义
                
        (1) `别名` 不引入新类型: String<Uchar> <=> String<unsigned char>
        
        (2) `常量表达式` -> 编译时求值: Buffer<char, 20-10> <=> Buffer<char, 10>
    

    2 错误检测: 语法、语义 等错误

        ——————————————————————————————————————————————————————————————————————————————
                    用 T / 用 T 的成员 / 接受 `T 类型 的 参数`
                      |\
                      |  含义: 3 点 
                      |
        与 `模板参数 使用`
        ——————————————————————————————————————————————————————————————————————————————
            (1) `相关` 的错误    |   最早可能 发现/检测 时刻         
                                |——————————————————————————————————————————————————                                         
                                |   [1] `编译时`: 模板 `第1次使用( 即 `给定` 了 `特定的模板实参`) 时 -> 该时刻: 实例化点` 
                                |   [2] `链接时`
        ——————————————————————————————————————————————————————————————————————————————
            (2) `无关` 的错误    |   [1] 编译时: 模板 `定义时(实例化 前)`
        ——————————————————————————————————————————————————————————————————————————————
    

    23.3 类模板 成员

    1 成员模板: 灵活性 & 控制度

        template<typename T>
        class complex
        {
            T re, im;
        public:
            template<typename U>
            complex(U r, U i)
                : re{r}, im{i} {}
        };
        
        成员模板函数 与 类模板 的 实例化版本 不是一一对应`, 还接受 extra 模板实参  
    

    (1) 成员模板 不能是 virtual

        否则, `传统 虚调用机制 的 vtbl 无法使用` -> 需变成 `动态增加(相应项)的 vtbl` 
        每当 user 用 `新实参类型` 调 `虚成员模板` 时,      
        `Linker` 必须向其所属 `类 / 类模板 的 特例化版本` 的 `vtbl 中 添加 1 个对应项` 
                    
            1] Linker 复杂
            2] 动态链接, 要与 传统技术 不同         
    

    (2) 成员类型: 若 只 use 部分( 而非全部 ) 模板参数, 则 "被迫" 依赖 全部模板参数 -> 解决: 让 所属类模板作 友元

        链表 List<T, Alloc> 的 链接类型 Link<T>
            List<double, My_allocator>::Link 与
            List<double, Your_allocator>::Link 不等价(含义上 应该等价 )   
                    
        template <...T, ... Alloc>  
        class List;
        
        template <...T>     
        class Link
        {
            template <...U, ... A>                                                           
            friend class List;                                                 
                                                                                                
            T val;          
            // ...                      
        };
        
        template <...T>
        struct Iterator 
        {
            Link<T>* currrent_pos;
        };
        
        template <...T, ... Alloc>  
        class List
        {
        public:
            Iterator<T> begin();
            // ...
        };
    

    2 友元

    (1) 友元模板函数 名后 的 空 <>: 模板类内 声明时, 必须带, 以指出 友元函数 是 模板; 否则, compile error

        模板类外 定义, 可省略
        
        template <... T> class Matrix;
        
        template <... T> 
        class Vector
        {
            T v[3];
        public:
            friend Vector operator*<>(const Matrix<T>&, const Vector&);             
        }
        
        template <... T> 
        class Matrix
        {
            T v[3];
        public:
            friend Vector<T> operator*<>(const Matrix&, const Vector<T>&);              
        }
        
        template <... T>    
        Vector<T> operator*(const Matrix<T>&, const Vector&)
        { /* ... */ } // ADL
    

    (2) 友元(模板)类 可以是:

    1) 已声明的 (非模板)类 / 模板类<模板参数> / 模板参数 / 类型别名 -> friend 之后 不能用 class(否则 compile error)

    2) 引入的新类 -> ... 必须用 class

        class C;        // 前向声明 
        using C2 = C;   // 类型别名 
        
        template <typename T>
        class A
        {
            friend C;        // [2]
            friend C2;       // [4]
            freind class C3; // [5]
        };
        
        template <typename T>
        class B
        {
            friend T;       // [1]      
            friend class T; // ERROR 
            friend A<T>;    // [3]
        };
    
    3   成员数据
    
        `non-static` 成员数据 `不能是 constexpr` 
    
    4   成员函数
        
        `成员模板函数 不能是 virtual(23.3.6) / 成员非模板函数 可以是`
    
        本身就是模板 / 因 类模板实例化 而成为 模板
    
    5   成员类型 别名
    
        (1) using / typedef 
            
        (2) 非侵入式添加类型
                                
            类型萃取 & 等价特性( auto / decltype() )`   // 28.1.4
            
        (3) 关联类型: 便于 类外访问
            
            容器 `迭代器 和 元素类型` 指定为 `别名`        
                                                        
            template<typename T>
            class Vector
            {
            public:
                using value_type = T;
                using iterator = Vector_iter<T>;
                // ...
            };
    

    23.4 函数模板

    1 函数模板 vs. 类模板

    1) 区别: 实参的确定
        
        [1] 函数模板 实参推断
            
        [2] 实例化 + 特例化   
            多 Ctor -> 类模板实参推断 不可行
    

    2) 联系: 用 推断出的类型 创建对象

    [1] 模板函数: 模板实参推断 -> internal: 用 函数形参 构造 模板类对象

        template <...T1, ... T2>
        pair<T1, T2> make_pair(T1 a, T2 b) 
        { return {a, b}; }
    

    [2] 用 类模板成员函数模板 的 arg 递归推断 类模板实参类型(T的真正类型)

    返回指向 (Tuple) 第N个元素的引用 --- 递归/转换为 --- 返回 指向 basePart(elem 序号在后) 第 N-1 个元素的引用

        template <typename Ret, typaname N>
        struct getNth
        {
            template<typaname T>
            static Ret& get(T t)
            {
                return getNth<Ret, N-1>::get(*t.base() );
            }
        };
        
        template <typename Ret>
        struct getNth<Ret, 0>
        {
            template<typaname T>
            static Ret& get(T t)
            {
                return t.x;
            }
        };
        Tuple<double, intm char> t{1.0, 2, 'a'};
        
        std::cout << get<0>(t);
    

    2 函数模板实参推断 若发挥不了作用, 就必须 显式指定模板实参

        template<...T>
        T* create(); 
        
        int* p = create<int>(); 
    

    2 函数模板 实参推断

    X 类型右值 被推断为 X
    X 类型左值 被推断为 X&

    (1) 引用推断: 类 保存 {整数, 指针} pair

        template <typename T>
        class Xref
        {
        private:
            int index;
            T* elem;
            bool owner;
        public:
            Xref(int i, T* p) // 保存 指针: Xref 是 owner
                : index{i}, elem{p}, owner{true} {}
                
            Xref(int i, T& r) // 保存 指向 r 的 指针, Xref 不是 owner
                : index{i}, elem{&r}, owner{false} {}
                
            Xref(int i, T&& r) // 将 r 移入 Xref, Xref 变为 owner
                : index{i}, elem{ new T{ move(r) } }, owner{true} {}
                
            ~Xref()
            {
                if(owner) delete elem;
            }
        };
        
        string x {"hello"};
    
        1) Xref<string> r1 {7, "hello"};
        2) Xref<string> r2 {7, x};
        3) Xref<string> r3 {7, new string{"hello"} }; // 调 Xref<string>(int i, string* p), r3 拥有 字符串 "hello"
            
        1)  Xref<string> r1 {7, "hello"};   
            
            匹配 
                template <typename T> 
                Xref<T>::Xref(int i, T&& r); 
                                        
            `X 类型` 的 `右值` 被         推断为 X
                |               |               |    
                |               |               |/
            string          "hello"      T = string
                                                        
                r: string&& 
                T: sting                        
                                        
                实例化版本                 
                Xref<string>::Xref(int i, string&& r);
                
        2) Xref<string> r2 {7, x};
         
            匹配
                template <typename T> 
                Xref<T>::Xref(int i, T& r); 
         
            `X 类型` 的 `左值` 被 推断为 X&
             |              |             |   
             |              |             | 
           string           x           string&
                    
                r: string& 
                T: sting 
                    
                实例化版本                      
                    Xref<string>::Xref(int i, string& r);
    

    (2) 实参转发 std::forward & 完美转发

                                    左值引用 / 右值引用 (int& / int&&) ...结果是 所引类型 (int)
                                    non-reference -> ...type 结果是 本身
                                        |
        template <typename T>           |
        T&& std::forward(typename remove_reference<T>::type& arg);
        
        template <typename T>
        T&& std::forward(typename remove_reference<T>::type&& arg);
        
        返回值: 
            static_cast<decltype(arg)&&>(arg)
            推导 arg 值的 左右值类型, 加上 && 
            => 转发时保持了 arg 的 左/右值属性: 是 左/右 值, 就按 左/右 值 调用 aother func
            
        template<class T>
        void wrapper(T&& arg) 
        {
            // arg is always lvalue
            foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T
        }
        
        arg 相应的实参是  arg 值类型  arg 的类型        实例化版本               T类型     调 std::forward      返回类型 
                                                                                        
            X类型的 右值 X           X&& 右值引用    void wrapper(X&& arg)   X       (::type&& arg)          X&&
                                                                                        
            X类型的 左值 X&          X&  右值引用    void wrapper(X& arg)    X&      (::type& arg)           X&
    

    (3) 希望 从 arg 构造出 T, 且 不产生 额外 copy: make_unique()

        template<typename T, typename Arg>
        unique_ptr<T> make_unique(int i, Arg&& arg)
        {                                                           
            return unique_ptr<T> { new T{i, forward<Arg>(arg) } };
        }   
    
    
        1) auto p1 = 
            make_unique<Xref<string> >(7, "hello");
                | template...
                | make_unique(int i, Arg&& arg)
                |
        string 类型 的 右值 "hello" 被 推断为 string
                |               
                |   `右值引用传递` / Arg = string
                |                       
        make_unique(int i, string&& arg)                
                
                unique_ptr<T> { new T{i, forward<Arg>(arg) } }  
                            |        |                          
                            |        |      
                    Xref<string>     |  调           
                                     |/
                                    Xref<string>::Xref(int, string&&)
                                                    
                                          
        2) auto p1 = 
            make_unique<Xref<string> >(7, x);
              | template...
              | make_unique(int i, Arg&& arg)
              |
        string 类型 的 左值 x 被 推断为 string&
              |             
              | `左值引用传递` / Arg = string&
              |                     
        make_unique(int i, string& arg)                 
            
            unique_ptr<T> { new T{i, forward<Arg>(arg) } }  
                        |        |              
                        |        |      
                Xref<string>     |  调           
                                 |/
                                Xref<string>::Xref(int, string&)
        
    3 函数模板 重载(12.3.1)
    

    23.5 模板 别名

        1 只能用 using 不能用 typedef 
    
        2 `不能定义` 别名的 `特例化版本`
    
        3 模板别名 使用时 
        
        ——————————————————————————————————————————————————————————————————————
        用到了 特例化 : 得 正确 特例化版本 
        ——————————————————————————————————————————————————————————————————————
        没用到 特例化 : 不能 将 `别名` 简单视为 原始模板中相应名 
                        template <int N>
                        using int_exact = typename int_exact_traits<N>::type; 
                            
                        int_exact 与 int_exact_traits<N>::type 行为不同              
        ——————————————————————————————————————————————————————————————————————
    

    23.6 源码组织: 不支持 分离编译

    1. C++ 不支持 模板 定义和使用 分离编译: 使用前 (只) 包含其声明 & other 编译单元 定义

    解决: 模板 使用前 (只) 包含其声明 & "稍后包含 模板定义" ( 在本编译单元 使用之后 )

        1) 特殊: 稍后 #include "模板定义源文件.cpp"
        
        2) 将 `模板实现 对 user 代码` 造成 `不良影响` 的机会 `降到最低`  
            
        3) 代价: `user code 中 宏` (等) 对 模板定义 可能造成影响                                        
    

    2 链接

    (1) 动态链接: 模板使用处 单一定义原则/ODR

    (2) 封装 复杂模板库 于 非模板接口函数

        double accum(const vector<double>& v)
        {
            return accumulate(v.begin(), v.end(), 0.0);
        }
        
        对外, 只需 该非模板接口函数声明 
        double accum(const vector<double>& v);
    
    函数模板实参推断: special 处.jpg 函数模板重载 与 派生 组合使用.jpg 用 函数模板 推断 类模板 实参类型: 第2个例子.jpg 接上图.jpg 模板源码组织 之 "稍后包含模板定义".jpg 建议.jpg

    相关文章

      网友评论

          本文标题:23章 模板

          本文链接:https://www.haomeiwen.com/subject/zzzwvrtx.html