c/c++

chapter3 去重复: type_traits / 可变参数

2022-06-18  本文已影响0人  my_passion

chapter3 去重复: type_traits / 可变参数模板

    编译期 获取 类型信息 
    
    1   type_traits
    
        编译期
            计算
            查询 
            判断: 检查 是否是 正确类型, 写出 更安全的代码
            转换
            选择: 一定程度消除 if-else switch-case, 降低 圈复杂度 
        
        编译期常量               std::integral_constant
        (模板)类型 判断/谓词    std::is_reference
        类型转换                std::remove_reference std::decay
        编译期 条件选择器       std::conditional 
        编译期获取 callableObj 的 函数调用运算符 的 return type: std::result_of 
        
    2   可变参数模板 
    
        C 中 可变参数函数 
        
        C++ 可变参数 模板 函数/类 
        
            往往和 type_traits 实现更强大的 编译期计算

3.1 type_traits <type_traits>

3.1.1 基本 traits

(1) 编译期常量  
        
    `编译期常量` 定义 & 获取 
    
    [1] static const 成员
            
        struct A 
        { 
            static const int value = 1; 
        };
        A::value
        
    [2] enum 成员 
            
        struct A 
        { 
            enum {value = 1}; 
        };
        A::value
        |
        |   C++11: 不必再额外定义变量 => 放 Base 类定义 
        |/
    [3] 只需从 std::integral_constant 派生 

        template <typename T>
        struct A: std::integral_constant<T, 1> {};
                                          |   |
                                          |/  |/
                                        type  value     
        A<int>::value
                                            
        编译期 true/false 类型   
            using true_type = std::integral_constant<bool, true>;
            using false_type = std::integral_constant<bool, false>;

        实现 std::integral_constant
        
                        类型 模板参数 
        namespace std         |\      值 模板参数
        {                     |     /
            template<typename T, T v>
            struct integral_constant
            {
                static const T value = v; // value 成员 
                
                using value_type = T;                 // 类型 模板参数 别名 
                using type = integral_constant<T, v>; // 自身别名
                
                // 类型转换运算符: 使得需要时发生  integral_constant<T, v> 到 value_type 的 隐式转换
                operator value_type() { return value; }
            };                              
        }

(2)  (模板)类型 判断/谓词 
    
    继承自 std::std::integral_constant
    
    judge 类型模板参数 是否为 某种类型 
    
    std::is_polymorphic / std::is_array / std::is_function / std::is_reference / std::is_const ...
    
    [1]
        template<class T>
        struct is_polymorphic;
            
            T 是否有 vf 
        
    [2] std::is_...::value == true/false 
        => 类型模板参数 T 是/否 为 `目标类型`
                    
(3) 2个 (模板)类型间关系 

    template<class T, class U> 
    struct is_same;
    
        std::cout << std::is_same<int, signed int>::value; // vs 下: 1
        
        类型严格相同 才认为 类型一致 
        
    template<class Base, class Derived> 
    struct is_base_of;
    
        class B...
        class D...
        std::is_base_of<B, D>::value
                        
    
(4) 类型转换: 移除(remove)/添加(add) reference pointer const (const volatile) & 移除数组 顶层(第1)/所有维度 & decay
    
    ::type 成员 是输出 类型
    
    [1] template<typaname T>
        struct remove_reference;
    
    [2] remove_pointer 
    
    [3] remove_const            
            
    [4] remove_cv               
        
    [5] remove_extent 
        remove_all_extents 
        
        std::cout << std::is_same<int[2], std::remove_extent<int[][2]>::type >::value << std::endl;   // vs 下: 1
        std::cout << std::is_same<int, std::remove_all_extents<int[][2]>::type >::value << std::endl; // vs 下: 1
    
    [5] decay
        
        template<typename T>
        ...std::decay<T>

        1] 移除 T 的引用 -> U
            using U = std::remove_reference<T>::type
        
        2] if U 为 数组: std::is_array<U>::value == true, 去 顶层维度, 加指针
            remove_extent<U>::type* 
            
        3] else if U 为 函数: std::is_function<U>::value == true, add_pointer 
            add_pointer<U>::type 
            
        4] else, remove_cv<U>::type 

            using T1 = typename std::decay<int[2]>::type;       // int*
            using T2 = typename std::decay<int(int)>::type;     // int(*)(int)
            using T3 = typename std::decay<int&&>::type;        // int
            using T4 = typename std::decay<const int&>::type;   // int
    
    // ===1. remove/add reference 
    创建 智能指针: 模板参数 T 的 原始类型 U
        std::remove_reference 移除 T 可能的引用
    
    获取 智能指针 所指对象(的引用)
        增加左值引用 
    
    #include <iostream>
    #include <type_traits>
    #include <memory>

    template <typename T>
    class Sp
    {
    public:
        using U = typename std::remove_reference<T>::type;
        
    private:
        std::unique_ptr<U> uptr;

    public:
        Sp() : uptr(new U(1) ) {}

        typename std::add_lvalue_reference<U>::type
        get() const
        {
            return *uptr.get();
        }
    };

    int main()
    {
        Sp<int> sp;
        int a = sp.get();
        std::cout << a << std::endl; // 1
    }
        
        
    // ===2. decay 的引入
    简化 remove_reference + remove_cv
    
    创建 模板参数 T 的 原始类型 U 的 heap 对象 
    
    T cv 的 引用 类型 => 直接用 new T() 编译报错 
    template<typename T>
    T* create()
    {
        return new T();
    }

    int* p = create<const volatile int&>(); 
        |
        | 解决
        |/
    remove_reference + remove_cv 
        
    #include <iostream>
    #include <type_traits>
    #include <memory>

    template<typename T>
    typename std::remove_cv< typename std::remove_reference<T>::type >::type*
    create()
    {
        using U = typename std::remove_cv< typename
            std::remove_reference<T>::type >::type;
        
        return new U();
    }

    int main()
    {
        int* p = create<const volatile int&>(); 
    }   
        |
        | 简化 remove_reference + remove_cv
        |/
    #include <iostream>
    #include <type_traits>
    #include <memory>

    template<typename T>
    typename std::decay<T>::type*
    create()
    {
        using U = typename std::decay<T>::type;
        return new U();
    }

    int main()
    {
        int* p = create<const volatile int&>(); 
    }   
        
    // ===3. decay 用于函数 
    
    将 函数(函数类型 无法保存) 变为 函数指针 -> 保存 函数指针 -> 延迟执行
        
    #include <iostream>
    #include <type_traits>

    template<typename F>
    struct FuncWrapper
    {
        using FT = typename std::decay<F>::type;

        FT pf;

        FuncWrapper(F& f_) : pf(f_)
        {

        }

        void call() { pf(); }
    };

    void f() { std::cout << "hello\n"; }

    using FuncType = void();

    int main()
    {
        // 函数名 被编译器视为 函数指针, f 类型: void(*)()
        FuncWrapper<FuncType> fw(f); 
        fw.call();
    }

3.1.2 编译期 条件选择器: std::conditional

    据 判断式 B == true/false 从 2个类型 T1, T2 中 选 1个 T1/T2
        
    template<bool B, typename T1, typename T2>
    std::conditional;

    using T = std::conditional<std::is_const<const int>::value, long, int>::type; // long 

3.1.3 编译期获取 callableObj 的 函数调用运算符 的 return type: std::result_of

(1) 引入 
    
    编译时推断 获取 callableObj 的 函数调用运算符(表达式) 的 return type
        Note: 不真正调用 函数调用运算符 

    [1] auto + decltype
    
        0 -> 强转为 T ptr (T*)0 -> 解引用 *(T*)0 得 T 对象: undefined value 
                                                            
        template<typename F, typename Arg>                          
        decltype( ( *(F*)0 )( *(Arg*)0 )  ) 
        Func(F f, Arg arg) 
        { 
            return f(arg); 
        }   
    
    [2] 后置返回类型 auto + decltype

        template<typename F, typename Arg>                          
        auto Func(F f, Arg arg) -> decltype( f(arg) )
        { 
            return f(arg); 
        }       
        
        问题 
            [1] F 无 模板参数 时, 不能用 decltype( f(arg) ) 获取类型
            
            [2] F 无 默认 Ctor 时, 不能用 F() 推导 F 成员函数的 return type 
            
        #include <iostream>
        #include <type_traits>  // std::result_of
        #include <utility>      // std::declval

        class F
        {
            F() = delete;
        public:
            int operator()(int i)
            {
                return i;
            }
            int foo() const { return 1; }
        };

        template<typename F, typename Arg>
        auto Func(F f, Arg arg) -> decltype(f(arg))
        {
            return f(arg);
        }

        int main()
        {
            // decltype( F()(1) ) i = 2; // F() 导致 编译报错 

            decltype(std::declval<F>()(std::declval<int>())) i = 2;

            decltype(std::declval<F>().foo()) j = 3;

            std::result_of<F(int)>::type k = 5;
        }
                    
    [3] std::declval 
            
    思路
        decltye( std::declval<F>()(std::declval<int>() ) ) i = 3;
            
        std::declval<F/int>()
            获取 任意类型 (F/int) 的 临时值 引用 
            Cannot be called => never returns a value
            
        decltype: `不求值, 只推断 expr 返回类型` 
                    
        decltye( std::declval<F>()( std::declval<int>() ) )
            |
            |   等价于 
            |/
        std::result_of<F(int)>::type 

    [4] std::result_of 
        
        template<typename CallableObj, typename... ArgTypes>
        class result_of< CallableObj(ArgTypes...) >;
            
                someCallableObj::operator()(someArgTypes...)
                                        |\
                                        |
        std::result_of< someCallableObj(someArgTypes...) >::type 
                
        #include <iostream>
        #include <type_traits>  // std::result_of

        int f(int) { return 1; }

        int main()
        {
            using fp = int(*)(int); // 函数指针 类型

            using T1 = std::result_of<decltype(f)& (int)>::type; // T1 == int                       
            using T2 = std::result_of<fp(int)>::type;            // T2 == int 

            T1 i;
            T2 j;
        }

3.2 可变参数模板

3.2.0 C 中 可变参数函数

2点可变
    参数 数量可变
    参数 类型可变

实现: <stdarg.h> 4 个 宏

    va_list     存储可变参数信息
    va_arg      获取 next 参数, 并自动后移1个位置
    va_start    开始使用 可变参数列表
    va_end      结束使用 可变参数列表

    #include <stdarg.h>
    #include <stdio.h>

    double sum(int n, ...)
    {
        double sum = 0.0;                   

        va_list ptr;            

        va_start(ptr, n);   

        for (int i = 0; i < n; i++)
        {
            sum += va_arg(ptr, double); // 指定 形参类型
        }                                   

        va_end(ptr);            

        return sum;
    }

    int main(int argc, char** argv)
    {
        printf("%lf\n", sum(3, 1.0, 2.0, 3.0) );
    }

局限: 程序中必须 显式告诉 compiler 可变参数 个数/类型。 用 第1实参 告诉 个数, other 实参 与 指定形参类型 去匹配

C++ 可变参模板 怎么做到 不告诉 compiler 参数个数 和 参数类型?依赖于

[1] 函数重载, 依靠参数 的 pattern 去 匹配 对应函数

[2] 函数模板 实参推断

[3] 类模板, 基于 偏特化(partial specialization) 来选择不同的实现

typename... Args: template parameter pack, 表明有多种 type

Args... tail: function parameter pack, 表明有多个参数

tail...: pack expansion

    #include <cstdarg>
    #include <iostream>

    template<typename T>
    double sum(T t)
    {
        return t;
    }
    template<typename T, typename ... Args>
    double sum(T t, Args... tail)
    {
        return sum(t) + sum(tail...);
    }

    int main(int argc, char** argv)
    {
        std::cout << sum(1.0, 2.0, 3.0) << std::endl;
    }

3.2.1 可变参数 模板函数

(1) 递归方式 展开参数包 

    参数包 Args... 展开过程中 递归调用, 
    每 调用1次 参数包中参数少1个, 直到所有参数都展开

    #include <iostream> 

    // 递归终止函数: 可以有多种版本
    // === version1 
    void print()
    {
        std::cout << "empty\n";
    }

    // 参数包展开函数 
    template <typename T, typename ...Args>
    void print(T t, Args... tail)
    {
        std::cout << t << std::endl;
        print(tail...);
    }

    int main()
    {
        print(1, 2.3, "hello");
    }

    递归调用过程 
    print(1, 2.3, "hello");
    print(2.3, "hello");
    print("hello");
    print();

    // === version2
    template <typename T>
    void print(T t)
    {
        std::cout << t << std::endl;
    }

    递归调用过程 
    print(1, 2.3, "hello");
    print(2.3, "hello");
    print("hello");

(2) 应用
    
    可变参数的 工厂函数
    
    #include <iostream> 

    class A
    {
    public:
        A(int) {}
    };

    class B
    {
    public:
        B(int, double) {}
    };

    template<typename T, typename... Args>
    T* getInstance(Args&&... args)
    {
        return new T(std::forward<Args>(args)...);
    }

    int main()
    {
        A* pa = getInstance<A>(1);

        B* pb = getInstance<B>(1, 2.0);
    }

3.2.1 可变参数 模板类

    编译期计算 参数包 中 参数类型的 size 和
    
    #include <iostream>

    // 前向声明 
    template<typename... Args> struct A;

    // 特化的递归终止类
    template<typename Last> 
    struct A<Last>
    {
        enum { value = sizeof(Last) };
    };

    // 部分展开的 可变参数模板类
    template<typename First, typename... Tails>
    struct A<First, Tails...>
    {
        enum { value = A< First>::value + A<Tails...>::value };
    };

    int main()
    {
        std::cout << A<int, double>::value << std::endl;
    }
上一篇 下一篇

猜你喜欢

热点阅读