在实现可变参数列表中的类型统计功能前,我们先看看下面代码中的需求场景:
/** * @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; }