1. 问题背景
在 C++11 的标准中,我们可以通过 std::get< Index >(tuple)
(以常量整数值为索引号)操作 tuple 中的参数,而到了 C++14 之后的标准,新增了 std::get< Type >(tuple)
(以数据类型为索引)的方式操作 tuple 中的参数。那么,若只是在 C++11 标准中,是否有办法使用 以数据类型为索引 的方式操作 tuple 中的参数呢?
2. 解决办法
解决上面所提到的问题,其本质上,就是要解决 如何获取 tuple 参数列表中指定数据类型的索引位置 的问题。凭借这几天我积累到经验(可参看我前几篇关于 tuple 的文章),设计了以下代码中的几个模板类,有效的解决了该问题,请先详细参看如下代码:
/**
* @struct X_type_index
* @brief 类型索引模板:从变参列表中查找指定类型的索引位置(以 < _Fy, _Indx > 结对进行判断)。
*
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param[in ] _Ty... : 变参列表。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index;
/**
* @struct X_type_index_iter
* @brief 协助 X_type_index 进行 变参列表 递归遍历操作。
*
* @param[in ] _Vy : 模板特化的参数;
* 为 true 时,转向 X_type_index_jter 继续进行递归遍历;
* 为 false 时,转回 X_type_index 继续进行递归遍历。
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param[in ] _Ty... : 变参列表。
*/
template< bool _Vy, typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter;
/**
* @struct X_type_index_jter
* @brief 协助 X_type_index_iter 进行 变参列表 递归遍历操作。
*
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引);
* 为 0 时,终止递归下降,否则转回 X_type_index 继续进行递归遍历。
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Ty... : 变参列表。
*/
template< size_t _Indx, typename _Fy, typename... _Ty >
struct X_type_index_jter;
/**
* @brief 终止 X_type_index_jter 递归下降。
*/
template< typename _Fy, typename... _Ty >
struct X_type_index_jter< 0, _Fy, _Ty... >
{
enum { value = 0 };
};
/**
* @brief 转回 X_type_index 继续进行递归下降遍历。
*/
template< size_t _Indx, typename _Fy, typename... _Ty >
struct X_type_index_jter
{
enum { value = 1 + X_type_index< _Fy, _Indx - 1, _Ty... >::value };
};
/**
* @brief 转向 X_type_index_jter 继续进行递归遍历。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter< true, _Fy, _Indx, _Ty... >
{
enum { value = 0 + X_type_index_jter< _Indx, _Fy, _Ty...>::value };
};
/**
* @brief 转回 X_type_index 继续进行递归遍历。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter< false, _Fy, _Indx, _Ty... >
{
enum { value = 1 + X_type_index< _Fy, _Indx, _Ty... >::value };
};
/**
* @brief X_type_index 递归遍历入口。
*/
template< typename _Fy, size_t _Indx, typename _Hy, typename... _Ty >
struct X_type_index< _Fy, _Indx, _Hy, _Ty... >
{
enum { value = X_type_index_iter< std::is_same< _Fy, _Hy >::value, _Fy, _Indx, _Ty... >::value };
};
/**
* @brief X_type_index 递归遍历终结位置。
*/
template< typename _Fy, size_t _Indx >
struct X_type_index< _Fy, _Indx >
{
enum { value = 0x1FFFFFFF };
};
/**
* @brief 从 tuple 的变参列表中查找指定类型的索引位置(以 < _Fy, _Indx > 结对进行操作)。
*
* @param [in ] _Fy : 待查找类型。
* @param [in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param [in ] _Ty : tuple 的参数列表。
*
* @return size_t
* - 返回索引位置;
* - 若返回值大于等于 X_type_index< _Fy, _Indx >::value[0x1FFFFFFF] 时,表示未找到查找的类型。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
constexpr size_t X_tuple_type_index(const std::tuple< _Ty... > &)
{
return X_type_index< _Fy, _Indx, _Ty... >::value;
}
以上代码中,使用 std::is_same< _Fy, _Hy >::value
判断数据类型是否一致,然后特化 X_type_index_iter<> 和 X_type_index_jter<> 这两个模板类进行递归推导操作,过程中,通过逐步累积索引值到 value 中,最后得到我们需要类型索引值。这过程,如下面的流程图所示:
3. 使用示例
下面给出完成的测试程序代码,亦可作为使用示例的参看代码:
#include <type_traits>
#include <tuple>
#include <iostream>
/**
* @struct X_type_index
* @brief 类型索引模板:从变参列表中查找指定类型的索引位置(以 < _Fy, _Indx > 结对进行判断)。
*
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param[in ] _Ty... : 变参列表。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index;
/**
* @struct X_type_index_iter
* @brief 协助 X_type_index 进行 变参列表 递归遍历操作。
*
* @param[in ] _Vy : 模板特化的参数;
* 为 true 时,转向 X_type_index_jter 继续进行递归遍历;
* 为 false 时,转回 X_type_index 继续进行递归遍历。
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param[in ] _Ty... : 变参列表。
*/
template< bool _Vy, typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter;
/**
* @struct X_type_index_jter
* @brief 协助 X_type_index_iter 进行 变参列表 递归遍历操作。
*
* @param[in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引);
* 为 0 时,终止递归下降,否则转回 X_type_index 继续进行递归遍历。
* @param[in ] _Fy : 待查找类型。
* @param[in ] _Ty... : 变参列表。
*/
template< size_t _Indx, typename _Fy, typename... _Ty >
struct X_type_index_jter;
/**
* @brief 终止 X_type_index_jter 递归下降。
*/
template< typename _Fy, typename... _Ty >
struct X_type_index_jter< 0, _Fy, _Ty... >
{
enum { value = 0 };
};
/**
* @brief 转回 X_type_index 继续进行递归下降遍历。
*/
template< size_t _Indx, typename _Fy, typename... _Ty >
struct X_type_index_jter
{
enum { value = 1 + X_type_index< _Fy, _Indx - 1, _Ty... >::value };
};
/**
* @brief 转向 X_type_index_jter 继续进行递归遍历。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter< true, _Fy, _Indx, _Ty... >
{
enum { value = 0 + X_type_index_jter< _Indx, _Fy, _Ty...>::value };
};
/**
* @brief 转回 X_type_index 继续进行递归遍历。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
struct X_type_index_iter< false, _Fy, _Indx, _Ty... >
{
enum { value = 1 + X_type_index< _Fy, _Indx, _Ty... >::value };
};
/**
* @brief X_type_index 递归遍历入口。
*/
template< typename _Fy, size_t _Indx, typename _Hy, typename... _Ty >
struct X_type_index< _Fy, _Indx, _Hy, _Ty... >
{
enum { value = X_type_index_iter< std::is_same< _Fy, _Hy >::value, _Fy, _Indx, _Ty... >::value };
};
/**
* @brief X_type_index 递归遍历终结位置。
*/
template< typename _Fy, size_t _Indx >
struct X_type_index< _Fy, _Indx >
{
enum { value = 0x1FFFFFFF };
};
/**
* @brief 从 tuple 的变参列表中查找指定类型的索引位置(以 < _Fy, _Indx > 结对进行操作)。
*
* @param [in ] _Fy : 待查找类型。
* @param [in ] _Indx : 待查找类型在变参列表中的第几个(以 0 为起始索引)。
* @param [in ] _Ty : tuple 的参数列表。
*
* @return size_t
* - 返回索引位置;
* - 若返回值大于等于 X_type_index< _Fy, _Indx >::value[0x1FFFFFFF] 时,表示未找到查找的类型。
*/
template< typename _Fy, size_t _Indx, typename... _Ty >
constexpr size_t X_tuple_type_index(const std::tuple< _Ty... > &)
{
return X_type_index< _Fy, _Indx, _Ty... >::value;
}
int main(int argc, char * argv[])
{
using _Tuple = std::tuple< int, int, int, char, float, double >;
//======================================
// 获取索引号
std::cout << "< int , 0 > : " << X_type_index< int , 0 >::value << std::endl;
std::cout << "< int , 0 > : " << X_type_index< int , 0, int >::value << std::endl;
std::cout << "< char , 0 > : " << X_type_index< char, 0, int, int >::value << std::endl;
std::cout << "< int , 1 > : " << X_type_index< int , 1, int, int, char >::value << std::endl;
std::cout << "< char , 0 > : " << X_type_index< char, 0, int, int, char, char >::value << std::endl;
std::cout << "< char , 1 > : " << X_type_index< char, 1, int, int, char, char, char >::value << std::endl;
std::cout << "tuple type = std::tuple< int, int, int, char, float, double >" << std::endl;
std::cout << "< int , 1 > : " << X_tuple_type_index< int , 1 >(_Tuple{}) << std::endl;
std::cout << "< int & , 1 > : " << X_tuple_type_index< int & , 1 >(_Tuple{}) << std::endl;
std::cout << "< char , 1 > : " << X_tuple_type_index< char , 1 >(_Tuple{}) << std::endl;
std::cout << "< double, 1 > : " << X_tuple_type_index< double, 1 >(_Tuple{}) << std::endl;
std::cout << "< float , 1 > : " << X_tuple_type_index< float , 1 >(_Tuple{}) << std::endl;
std::cout << "< void *, 1 > : " << X_tuple_type_index< void *, 1 >(_Tuple{}) << std::endl;
//======================================
// 按 数据类型方式索引 tuple 的参数
_Tuple xtuple{ 100, 200, 300, 'A', 1.234F, 1.234 };
std::cout << " xtpule< 0 > = " << std::get< 0 >(xtuple) << std::endl;
std::cout << " xtpule< 1 > = " << std::get< 1 >(xtuple) << std::endl;
std::cout << " xtpule< 2 > = " << std::get< 2 >(xtuple) << std::endl;
std::cout << " xtpule< 3 > = " << std::get< 3 >(xtuple) << std::endl;
std::cout << " xtpule< 4 > = " << std::get< 4 >(xtuple) << std::endl;
std::cout << " xtpule< 5 > = " << std::get< 5 >(xtuple) << std::endl;
std::get< X_tuple_type_index< int , 0 >(xtuple) >(xtuple) = 101;
std::get< X_tuple_type_index< int , 1 >(xtuple) >(xtuple) = 201;
std::get< X_tuple_type_index< int , 2 >(xtuple) >(xtuple) = 301;
std::get< X_tuple_type_index< char , 0 >(xtuple) >(xtuple) = 'B';
std::get< X_tuple_type_index< float , 0 >(xtuple) >(xtuple) = 1.245F;
std::get< X_tuple_type_index< double, 0 >(xtuple) >(xtuple) = 1.236;
std::cout << " xtpule< int , 0 >[0] = " << std::get< 0 >(xtuple) << std::endl;
std::cout << " xtpule< int , 1 >[1] = " << std::get< 1 >(xtuple) << std::endl;
std::cout << " xtpule< int , 2 >[2] = " << std::get< 2 >(xtuple) << std::endl;
std::cout << " xtpule< char , 0 >[3] = " << std::get< 3 >(xtuple) << std::endl;
std::cout << " xtpule< float , 0 >[4] = " << std::get< 4 >(xtuple) << std::endl;
std::cout << " xtpule< double, 0 >[5] = " << std::get< 5 >(xtuple) << std::endl;
//======================================
return 0;
}