1. 程式人生 > >C++11 —— 獲取 tuple 引數列表中指定資料型別的索引位置

C++11 —— 獲取 tuple 引數列表中指定資料型別的索引位置

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 中,最後得到我們需要型別索引值。這過程,如下面的流程圖所示:

type_index

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;
}