tuple元組詳解
這次要講的內容是: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元組詳解