C++11 —— 獲取 tuple 引數列表中指定資料型別的索引位置
阿新 • • 發佈:2018-12-22
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; }