元組,c++11中引入的新的類型,可類比std::pair。 但是std::pair只能支持兩個元素。 理論上, 元組支持0~任意個元素。
本文演示環境: VS2015 up3
0、頭文件
#include <tuple>
1、創建和初始化
1.1、創建一個空的元組, 創建時,需要指定元組的數據類型。
std::tuple<int, float, double, long, long long> first;
1.2 、創建一個元組並初始化元組。
1 std::string str_second_1("_1"); 2 std::string str_second_2("_2"); 3 4 // 指定了元素類型為引用 和 std::string, 下面兩種方式都是可以的,只不過第二個參數不同而已 5 std::tuple<std::string, std::string> second_1(str_second_1, std::string("_2")); 6 std::tuple<std::string, std::string> second_2(str_second_1, str_second_2);
1.3、創建一個元素是引用的元組
1 //3、創建一個元組,元組的元素可以被引用, 這里以 int 為例 2 int i_third = 3; 3 std::tuple<int&> third(std::ref(i_third));
1.4、使用make_tuple創建元組
1 int i_fourth_1 = 4; 2 int i_fourth_2 = 44; 3 // 下面的兩種方式都可以 4 std::tuple<int, int> forth_1 = std::make_tuple(i_fourth_1, i_fourth_2); 5 auto forth_2 = std::make_tuple(i_fourth_1, i_fourth_2);
1.5、創建一個類型為引用的元組, 對元組的修改。 這里以 std::string為例
1 std::string str_five_1("five_1"); 2 // 輸出原址值 3 std::cout << "str_five_1 = " << str_five_1.c_str() << "\n"; 4 5 std::tuple<std::string&, int> five(str_five_1, 5); 6 // 通過元組 對第一個元素的修改,str_five_1的值也會跟着修改,因為元組的第一個元素類型為引用。 7 // 使用get訪問元組的第一個元素 8 std::get<0>(five) = "five_2"; 9 10 // 輸出的將是: five_2 11 std::cout << "str_five_1 = " << str_five_1.c_str() << "\n";
輸出結果(VS2015 up3輸出):
2、計算元組的元素個數
需要函數: std::tuple_size。 下面是一個例子,
1 std::tuple<char, int, long, std::string> first('A', 2, 3, "4"); 2 // 使用std::tuple_size計算元組個數 3 int i_count = std::tuple_size<decltype(first)>::value; 4 std::cout << "元組個數=" << i_count << "\n";
輸出結果:
3、訪問元素
訪問元組的元素,需要函數: std::get<index>(obj)。其中:【index】是元組中元素的下標,0 ,1 , 2, 3, 4,.... 【obj】-元組變量。
一個例子:
1 std::tuple<char, int, long, std::string> second('A', 2, 3, "4"); 2 int index = 0; 3 std::cout << index++ << " = " << std::get<0>(second) << "\n"; 4 std::cout << index++ << " = " << std::get<1>(second) << "\n"; 5 std::cout << index++ << " = " << std::get<2>(second) << "\n"; 6 std::cout << index++ << " = " << std::get<3>(second).c_str() << "\n";
輸出結果:
【注意】元組不支持迭代訪問, 且只能通過索引(或者tie解包:將元組的中每一個元素提取到指定變量中)訪問, 且索引不能動態傳入。上面的代碼中,索引都是在編譯器編譯期間就確定了。 下面的演示代碼將會在編譯期間出錯。
for (int i = 0; i < 3; i++) std::cout << index++ << " = " << std::get<i>(second) << "\n"; // 無法通過編譯
4、獲取元素的類型
獲取元組中某個元素的數據類型,需要用到另外一個類型: std::tuple_element 。 語法: std::tuple_element<index, tuple> 。 【index】-元組中元素的索引,【tuple】哪一個元組
一個例子:
1 std::tuple<int, std::string> third(9, std::string("ABC")); 2 3 // 得到元組第1個元素的類型,用元組第一個元素的類型聲明一個變量 4 std::tuple_element<1, decltype(third)>::type val_1; 5 6 // 獲取元組的第一個元素的值 7 val_1 = std::get<1>(third); 8 std::cout << "val_1 = " << val_1.c_str() << "\n";
輸出結果:
用獲取元組第一個元素的類型聲明了一個變量,再對變量賦值。
5、使用 std::tie解包
元組,可以看作一個包,類比結構體。 需要訪問元組的元素時,2 種方法: A、索引訪問,B、std::tie 。
元組包含一個或者多個元素,使用std::tie解包: 首先需要定義對應元素的變量,再使用tie。 比如,元素第0個元素的類型時 char, 第1個元素類型時int, 那么, 需要定義 一個 char的變量和int的變量, 用來儲存解包元素的結果。
一個例子:
1 std::tuple<char, int, long, std::string> fourth('A', 2, 3, "4"); 2 3 // 定義變量,保存解包結果 4 char tuple_0 = '0'; 5 int tuple_1 = 0; 6 long tuple_2 = 0; 7 std::string tuple_3(""); 8 9 // 使用std::tie, 依次傳入對應的解包變量 10 std::tie(tuple_0, tuple_1, tuple_2, tuple_3) = fourth; 11 12 // 輸出解包結果 13 std::cout << "tuple_0 = " << tuple_0 << "\n"; 14 std::cout << "tuple_1 = " << tuple_1 << "\n"; 15 std::cout << "tuple_2 = " << tuple_2 << "\n"; 16 std::cout << "tuple_3 = " << tuple_3.c_str() << "\n";
輸出結果:
說到 std::tie , 看下 VS2015 up3中的定義:
1 template<class... _Types> inline 2 constexpr tuple<_Types&...> 3 tie(_Types&... _Args) _NOEXCEPT 4 { // make tuple from elements 5 typedef tuple<_Types&...> _Ttype; 6 return (_Ttype(_Args...)); 7 }
接着 std::tie 解包。 如果一個元組,只需要取出其中特定位置上的元素,不用把每一個元素取出來, 怎么做?
比如: 只要索引為 偶數的元素。
元組提供了類似占位符的功能: std::ignore 。滿足上面的需求,只需要在索引為奇數的位置填上 std::ignore 。 一個例子:
1 std::tuple<char, int, long, std::string> fourth('A', 2, 3, "4"); 2 3 // 定義變量,保存解包結果 4 char tuple_0 = '0'; 5 int tuple_1 = 0; 6 long tuple_2 = 0; 7 std::string tuple_3(""); 8 9 // 使用占位符 10 std::tie(tuple_0, std::ignore, tuple_2, std::ignore) = fourth; 11 12 // 輸出解包結果 13 std::cout << "tuple_0 = " << tuple_0 << "\n"; 14 std::cout << "tuple_1 = " << tuple_1 << "\n"; 15 std::cout << "tuple_2 = " << tuple_2 << "\n"; 16 std::cout << "tuple_3 = " << tuple_3.c_str() << "\n";
輸出結果:
6、元組連接(拼接)
使用 std::tuple_cat 執行拼接
一個例子:
1 std::tuple<char, int, double> first('A', 1, 2.2f); 2 3 // 組合到一起, 使用auto, 自動推導 4 auto second = std::tuple_cat(first, std::make_tuple('B', std::string("-=+"))); 5 // 組合到一起,可以知道每一個元素的數據類型時什么 與 auto推導效果一樣 6 std::tuple<char, int, double, char, std::string> third = std::tuple_cat(first, std::make_tuple('B', std::string("-=+"))); 7 8 // 輸出合並后的元組內容 9 int index = 0; 10 std::cout << index++ << " = " << std::get<0>(second) << "\n"; 11 std::cout << index++ << " = " << std::get<1>(second) << "\n"; 12 std::cout << index++ << " = " << std::get<2>(second) << "\n"; 13 14 std::cout << index++ << " = " << std::get<3>(second) << "\n"; 15 std::cout << index++ << " = " << std::get<4>(second).c_str() << "\n";
輸出結果:
7、遍歷
這里將采用的時 遞歸遍歷,需要注意,考慮爆棧的情況。其實,tuple也是基於模板的STL容器。 因為其可以容納多個參數,且每個參數類型可不相同,遍歷輸出則涉及到參數展開的情況,這里以遞歸的方式實現遍歷, 核心代碼:
1 template<typename Tuple, size_t N> 2 struct tuple_show 3 { 4 static void show(const Tuple &t, std::ostream& os) 5 { 6 tuple_show<Tuple, N - 1>::show(t, os); 7 os << ", " << std::get<N - 1>(t); 8 } 9 }; 10 11 12 // 偏特性,可以理解為遞歸的終止 13 template<typename Tuple> 14 struct tuple_show < Tuple, 1> 15 { 16 static void show(const Tuple &t, std::ostream &os) 17 { 18 os << std::get<0>(t); 19 } 20 }; 21 22 23 24 // 自己寫個函數,調用上面的遞歸展開, 25 template<typename... Args> 26 std::ostream& operator << (std::ostream &os, const std::tuple<Args...>& t) 27 { 28 os << "["; 29 tuple_show<decltype(t), sizeof...(Args)>::show(t, os); 30 os << "]"; 31 32 return os; 33 }
調用示例:
1 auto t1 = std::make_tuple(1, 'A', "-=+", 2); 2 std::cout << t1;
輸出結果: