• C++11 —— 统计 tuple 中指定数据类型的数量


    问题背景

      在实现可变参数列表中的类型统计功能前,我们先看看下面代码中的需求场景:

    /**
     * @struct x_selector_t< size_t >
     * @brief  协助 make_task() 接口的特化选择功能的辅助类。
     */
    template< size_t >
    struct x_selector_t
    {
    
    };
    
    /**
     * @brief xtuple 参数列表中未包含指定数据类型的时候,创建 x_task_A_t 对象。
     */
    template< typename... _Args >
    x_task_t * make_task(const x_selector_t< 0 > &, std::tuple< _Args... > && xtuple)
    {
        using _Tuple = typename std::tuple< _Args... >;
        return (new x_task_A_t(std::forward< _Tuple >(xtuple)));
    }
    
    /**
     * @brief xtuple 参数列表中包含指定数据类型 1 个的时候,创建 x_task_B_t 对象。
     */
    template< typename... _Args >
    x_task_t * make_task(const x_selector_t< 1 > &, std::tuple< _Args... > && xtuple)
    {
        using _Tuple = typename std::tuple< _Args... >;
        return (new x_task_B_t(std::forward< _Tuple >(xtuple)));
    }
    
    /**
     * @brief xtuple 参数列表中包含指定数据类型 2 个的时候,创建 x_task_C_t 对象。
     */
    template< typename... _Args >
    x_task_t * make_task(const x_selector_t< 2 > &, std::tuple< _Args... > && xtuple)
    {
        using _Tuple = typename std::tuple< _Args... >;
        return (new x_task_C_t(std::forward< _Tuple >(xtuple)));
    }
    
    

      上面的代码中,x_task_A_t, x_task_B_t, x_task_C_t 都是 x_task_t 的派生类,按照上面已经提供的三个 make_task() 接口,我们能不能只提供一个接口(如下面的代码所示),就可自动选择这三个接口之一创建 x_task_t 对象呢?当然可以,只要我们解决 “怎么统计 tuple 中指定数据类型的数量” 这个问题就能办到。

    /**
     * @brief 自动统计 _Ty 数据类型在 xtuple 参数列表中的数量,
     *        然后选择对应的 make_task() 接口,创建 x_task_t 对象。
     */
    template< typename _Ty, typename... _Args >
    x_task_t * create_task(const x_selector_t< 2 > &, std::tuple< _Args... > && xtuple)
    {
        using _Tuple = typename std::tuple< _Args... >;
    
        constexpr size_t const xty_count = ...; // 统计 _Ty 在 _Args... 参数列表中的数量
    
        // 当前只有三种方式创建对象,增加编译期的断言判断
        static_assert(xty_count < 3, "There are currently only three ways to create x_task_t!");
    
        // 使用 x_selector_t< xty_count >() 特化对象,进行接口选择
        return make_task(x_selector_t< xty_count >(), std::forward< _Tuple >(xtuple)));
    }
    

    解决办法

      为解决可变参数列表种的类型统计问题,我们可以利用 C++11 的可变参数模板类的自动推导的特性,实现这一功能,请看下面代码所给出了解决的方案:

    
    /** X_type_count 的前置声明 */
    template< typename _Fy, typename... _Ty >
    struct X_type_count;
    
    /**
     * @struct X_type_count< _Fy, _Hy, _Ty... >
     * @brief  递归的数据类型统计类。
     */
    template< typename _Fy, typename _Hy, typename... _Ty >
    struct X_type_count< _Fy, _Hy, _Ty... >
    {
        enum { value = (int)(std::is_same< _Fy, _Hy >::value) + X_type_count< _Fy, _Ty... >::value };
    };
    
    /**
     * @struct X_type_count< _Fy >
     * @brief  终止递归的数据类型统计类。
     */
    template< typename _Fy >
    struct X_type_count< _Fy >
    {
        enum { value = 0 };
    };
    
    /**
     * @brief 统计 tuple 对象内某个数据类型的数量。
     */
    template< typename _Fy, typename... _Ty >
    constexpr size_t X_tuple_type_count(const std::tuple< _Ty... > &)
    {
        return X_type_count< _Fy, _Ty... >::value;
    }
    
    

      细看 X_type_count< _Fy, _Hy, _Ty... >::value 值的实现方式,我们用标准库提供的 std::is_same< _Fy, _Hy >::value 进行类型判断,将判断结果的 bool 值强制转换为为整数(0 或 1),然后累加到 value 中;下一步再以递归方式累加 X_type_count< _Fy, _Ty... >::value 的值,最后逐步地统计到整个可变参数列表中指定类型的数量。

      完整的测试代码如下:

    #include <type_traits>
    #include <tuple>
    #include <iostream>
    
    ////////////////////////////////////////////////////////////////////////////////
    
    /** X_type_count 的前置声明 */
    template< typename _Fy, typename... _Ty >
    struct X_type_count;
    
    /**
     * @struct X_type_count< _Fy, _Hy, _Ty... >
     * @brief  递归的数据类型统计类。
     */
    template< typename _Fy, typename _Hy, typename... _Ty >
    struct X_type_count< _Fy, _Hy, _Ty... >
    {
        enum { value = (int)(std::is_same< _Fy, _Hy >::value) + X_type_count< _Fy, _Ty... >::value };
    };
    
    /**
     * @struct X_type_count< _Fy >
     * @brief  终止递归的数据类型统计类。
     */
    template< typename _Fy >
    struct X_type_count< _Fy >
    {
        enum { value = 0 };
    };
    
    /**
     * @brief 统计 tuple 对象内某个数据类型的数量。
     */
    template< typename _Fy, typename... _Ty >
    constexpr size_t X_tuple_type_count(const std::tuple< _Ty... > &)
    {
        return X_type_count< _Fy, _Ty... >::value;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    
    int main(int argc, char * argv[])
    {
        std::cout << "X_type_count< int, _Ty... > : "
                  << X_type_count< int, int, int, char, char, double >::value << std::endl;
    
        using _XTuple = std::tuple< int, int, int, char, char, double >;
        std::cout << "tuple type : std::tuple< int, int, int, char, char, double > " << std::endl;
        std::cout << "X_tuple_type_count< int    > : " << X_tuple_type_count< int    >(_XTuple{}) << std::endl;
        std::cout << "X_tuple_type_count< char   > : " << X_tuple_type_count< char   >(_XTuple{}) << std::endl;
        std::cout << "X_tuple_type_count< double > : " << X_tuple_type_count< double >(_XTuple{}) << std::endl;
        std::cout << "X_tuple_type_count< float  > : " << X_tuple_type_count< float  >(_XTuple{}) << std::endl;
        std::cout << "X_tuple_type_count< void * > : " << X_tuple_type_count< void * >(_XTuple{}) << std::endl;
    
        return 0;
    }
    
    

      输出结果如下:

    X_type_count< int, _Ty... > : 2
    tuple type : std::tuple< int, int, int, char, char, double >
    X_tuple_type_count< int    > : 3
    X_tuple_type_count< char   > : 2
    X_tuple_type_count< double > : 1
    X_tuple_type_count< float  > : 0
    X_tuple_type_count< void * > : 0
    
  • 相关阅读:
    Docker界面化管理
    搭建MQTT服务器(Docker版)
    VS Code Markdown文件实时预览
    Nginx直接处理接口请求,返回相应内容(带html标签)
    Docker(九): 安装MySQL主从复制
    feign的一个注解居然隐藏这么多知识!
    使用Magisk指令修改 ro.debuggable(不刷机)
    【钓鱼可用】文件名反转字符串
    android高级UI之贝塞尔曲线<下>--贝塞尔曲线运用:QQ消息气泡
    英文阅读技巧操练---Article 1:The Product-Minded Software Engineer《一》
  • 原文地址:https://www.cnblogs.com/Gaaagaa/p/12130416.html
Copyright © 2020-2023  润新知