1. 程式人生 > >tuple元組詳解

tuple元組詳解

sta 把他 tle div main opened ble 進行 模板

這次要講的內容是:c++11中的tuple(元組)。tuple看似簡單,其實它是簡約而不簡單,可以說它是c++11中一個既簡單又復雜的東東,關於它簡單的一面是它很容易使用,復雜的一面是它內部隱藏了太多細節,要揭開它神秘的面紗時又比較困難。

  tuple是一個固定大小的不同類型值的集合,是泛化的std::pair。和c#中的tuple類似,但是比c#中的tuple強大得多。我們也可以把他當做一個通用的結構體來用,不需要創建結構體又獲取結構體的特征,在某些情況下可以取代結構體使程序更簡潔,直觀。

基本用法

構造一個tuple

tuple<const char*, int>tp = make_tuple(sendPack,nSendSize); //構造一個tuple

這個tuple等價於一個結構體

技術分享圖片
struct A
{
char* p;
int len;
};

用tuple<const char*, int>tp就可以不用創建這個結構體了,而作用是一樣的,是不是更簡潔直觀了。還有一種方法也可以創建元組,用std::tie,它會創建一個元組的左值引用。

auto tp = return std::tie(1, "aa", 2);
//tp的類型實際是:
std::tuple<int&,string&, int&>

再看看如何獲取它的值:

const char* data = tp.get<0>(); //獲取第一個值
int len = tp.get<1>(); //獲取第二個值

還有一種方法也可以獲取元組的值,通過std::tie解包tuple

int x,y;
string a;
std::tie(x,a,y) = tp; 

通過tie解包後,tp中三個值會自動賦值給三個變量。

解包時,我們如果只想解某個位置的值時,可以用std::ignore占位符來表示不解某個位置的值。比如我們只想解第三個值時:

std::tie(std::ignore,std::ignore,y) = tp; //只解第三個值了

還有一個創建右值的引用元組方法:forward_as_tuple。

std::map<int, std::string> m;
m.emplace(std::forward_as_tuple(10, std::string(20, ‘a‘)));

它實際上創建了一個類似於std::tuple<int&&, std::string&&>類型的tuple。

我們還可以通過tuple_cat連接多個tupe

技術分享圖片 技術分享圖片
int main()
{
std::tuple<int, std::string, float> t1(10, "Test", 
3.14);
int n = 7;
auto t2 = std::tuple_cat(t1, std::make_pair("Foo", 
"bar"), t1, std::tie(n));
n = 10;
print(t2);
}
技術分享圖片

輸出結果:

(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)

  到這裏tuple的用法介紹完了,是不是很簡單,也很容易使用,相信你使用它之後就離不開它了。我前面說過tuple是簡約而不簡單。它有很多高級的用法。它和模板元關系密切,要介紹它的高級用法的時候,讀者需要一定的模板元基礎,如果你只是把它當一個泛型的pair去使用時,這部分可以不看,如果你想用它高級用法的時候就往下看。讓我們要慢慢揭開tuple神秘的面紗。

tuple的高級用法

獲取tuple中某個位置元素的類型

  通過std::tuple_element獲取元素類型。

技術分享圖片
template<typename Tuple>
void Fun(Tuple& tp)
{
std::tuple_element<0,Tuple>::type first = std::get<0> 
(mytuple);
std::tuple_element<1,Tuple>::type second = std::get<1> 
(mytuple);
}
技術分享圖片

  

獲取tuple中元素的個數:

tuple t;

int size = std::tuple_size<decltype(t))>::value;

遍歷tuple中的每個元素

  因為tuple的參數是變長的,也沒有for_each函數,如果我們想遍歷tuple中的每個元素,需要自己寫代碼實現。比如我要打印tuple中的每個元素。

技術分享圖片 技術分享圖片
template<class Tuple, std::size_t N>
struct TuplePrinter {
    static void print(const Tuple& t)
    {
        TuplePrinter<Tuple, N - 1>::print(t);
        std::cout << ", " << std::get<N - 1>(t);
    }
};

template<class Tuple>
struct TuplePrinter<Tuple, 1>{
    static void print(const Tuple& t)
    {
        std::cout << std::get<0>(t);
    }
};

template<class... Args>
void PrintTuple(const std::tuple<Args...>& t)
{
    std::cout << "(";
    TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
    std::cout << ")\n";
}
技術分享圖片 技術分享圖片

根據tuple元素值獲取其對應的索引位置

技術分享圖片 技術分享圖片
namespace detail
{
    template<int I, typename T, typename... Args>
    struct find_index
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<I - 1>(t) == val) ? I - 1 :
                find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
        }
    };

    template<typename T, typename... Args>
    struct find_index<0, T, Args...>
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<0>(t) == val) ? 0 : -1;
        }
    };
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return detail::find_index<0, sizeof...(Args) - 1, T, Args...>::
           call(t, std::forward<T>(val));
}

int main()
{
    std::tuple<int, int, int, int> a(2, 3, 1, 4);
    std::cout << find_index(a, 1) << std::endl; // Prints 2
    std::cout << find_index(a, 2) << std::endl; // Prints 0
    std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}
技術分享圖片 技術分享圖片

展開tuple,並將tuple元素作為函數的參數。這樣就可以根據需要對tuple元素進行處理了

技術分享圖片 技術分享圖片
#include <tuple>
#include <type_traits>
#include <utility>

template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T && t, A &&... a)
-> decltype(Apply<N-1>::apply(
::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), 
::std::forward<A>(a)...
))
{
return Apply<N-1>::apply(::std::forward<F>(f), 
::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), 
::std::forward<A>(a)...
);
}
};

template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a)
-> decltype(::std::forward<F>(f) 
(::std::forward<A>(a)...))
{
return ::std::forward<F>(f)(::std::forward<A> 
(a)...);
}
};

template<typename F, typename T>
inline auto apply(F && f, T && t)
-> decltype(Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), 
::std::forward<T>(t)))
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), 
::std::forward<T>(t));
}

void one(int i, double d)
{
std::cout << "function one(" << i << ", " << d << 
");\n";
}
int two(int i)
{
std::cout << "function two(" << i << ");\n";
return i;
}

//測試代碼
int main()
{
std::tuple<int, double> tup(23, 4.5);
apply(one, tup);

int d = apply(two, std::make_tuple(2));

return 0;
}
技術分享圖片

  看到這裏,想必大家對tuple有了一個全面的認識了吧,怎麽樣,它是簡約而不簡單吧。對模板元不熟悉的童鞋可以不看tuple高級用法部分,不要為看不懂而捉急,沒事的,高級部分一般用不到,知道基本用法就夠用了。

tuple和vector比較:

vector只能容納同一種類型的數據,tuple可以容納任意類型的數據;

vector和variant比較:

二者都可以容納不同類型的數據,但是variant的類型個數是固定的,而tuple的類型個數不是固定的,是變長的,更為強大。

tuple元組詳解