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