tuple
是TR1引入的東西,它擴展了pair
的概念,擁有任意數量的元素。也就是說,tuple
呈現出一個異質元素列,其中每個元素類型都可以被指定,或來自編譯其推導。C++11中,variadic template被引入,使template可以接受任意數量的實參。在<tuple>
中的class tuple
的聲明爲
namespace std {
template <typename... Types>
class tuple;
}
tuple
的常用操作如下表
操作 | 含義 |
---|---|
tuple<T1,T2,...,Tn> t |
以n個給定類型的元素建立一個tuple ,以各元素類型的默認構造函數完成初始化(基礎類型的初值爲0) |
tuple<T1,T2,...,Tn> t(v1,v2,...,vn) |
以n個給定類型的元素建立一個tuple ,以給定值完成初始化 |
tuple<T1, T2> t(p) |
建立一個tuple ,帶有兩個元素,使用給定的pair p 的元素類型和值進行初始化 |
t=t2 |
將t2賦值給t |
t=p |
將pair p 賦值給一個帶有兩個元素的tuple |
t1==t2 |
比較兩個tuple 是否相等(如果所有元素的== 比較結果都是true ,就返回true ) |
t1!=t2 |
判斷t1與t2是否不相等(!(t1==t2) ) |
t1<t2 |
t1是否小於t2,使用字典式比較 |
t1>t2 |
t1是否大於t2 |
t1<=t2 |
t1 小於等於t2 |
t1>=t2 |
t1大於等於t2 |
t1.swap(t2) |
互換t1和t2的數據(始自C++11) |
swap(t1,t2) |
互換t1和t2的數據(全局函數,始自C++11) |
make_tuple(v1,v2,...) |
以傳入的所有數值和類型建立一個tuple ,並允許由此tuple 提取數值 |
tie(ref1,ref2,...) |
建立一個由reference構成的tuple,並允許由此tuple 提取個別數值 |
1. tuple
的操作
tuple
的接口比較直觀:
- 通過明白的聲明,或通過便捷函數
make_tuple()
,可以創建一個tuple
- 通過
get<>()
函數模板,可以訪問tuple
的元素
下面的語句創建一個異質的四元素tuple
tuple<string, int, int, complex<double>> t;
每個元素的內容由默認構造函數初始化,基礎類型都被初始化爲0(這項保證始自C++11)。下面的語句建立並初始化一個異質的三元素tuple
:
tuple<int, float, string> t1(41, 6.3, "nico");
也可以使用make_tuple()
建立tuple
,其所有元素類型都從初值自動推導,如
make_tuple(22, 44, "nico");
建立和初始化一個元素類型分別爲int
,int
,const char*
的tuple
。tuple
的元素類型可以是引用,如:
string s;
tuple<string&> t(s);
get<0>(t)="hello"; // s的值變爲"hello"
tuple
不是尋常的容器,不允許迭代元素。對於tuple
可以使用其成員函數來處理元素,因此必須在編譯期知道需要處理的元素的索引值。如可以這樣處理tuple t1
的第一元素:
get<0>(t1)
運行期才傳入一個索引值是不被允許的:
int i;
get<i>(t1) // 編譯錯誤,i不是編譯時的值
此外,tuple
還提供慣常的拷貝、賦值和比較操作。它們身上都允許發生隱式類型轉換,但元素個數比較絕對吻合。如果兩個tuple
的所有元素都相等,它們就整體相等。檢查某個tuple
是否小於另一個tuple
,採用的是lexicographical(字典編纂式的)比較法則。
2. 便捷函數make_tuple()
和tie()
便捷函數make_tuple()
會根據值建立tuple
,不需要明確指出元素類型。藉由特別的函數對象reference_wrapper<>
及便捷函數ref()
和cref()
(都是自C++11起被定義於<functional>
),可以影響make_tuple()
產生的類型,如以下表達式產生的tuple
帶有一個引用,指向對象s
:
string s;
make_tuple(ref(s)); // tuple<string&>
運用引用並搭配make_tuple()
,就可以提取tuple
的元素值,如
std::tuple <int, float, std::string> t(77, 1.1, "more light");
int i;
float f;
std::string s;
// 將t的值分配給i, f和s
std::make_tuple(std::ref(i), std::ref(f), std::ref(s)) = t;
如果想方便地在tuple
中使用引用,可以選擇tie()
,它可以建立一個內含引用的tuple
:
std::tuple <int, float, std::string> t(77,1.1,"more light");
int i;
float f;
std::string s;
std::tie(i,f,s) = t; // 將t的值分配給i, f和s
這裏的std::tie(i,f,s)
會以i, f和s的引用建立起一個tuple
,因此上述賦值操作其實就是將t內的元素分別賦值給i、f和s。
&ems;使用tie()
時,std::ignore
允許我們忽略````tuple中的某些元素,也就是說可以提取局部的
tuple```的元素值:
std::tuple <int, float, std::string> t(77,1.1,"more light");
int i;
std::string s;
std::tie(i,std::ignore,s) = t; // 將t的第一和第三個元素分配給i和s
3. tuple
和初值列(Initializer List)
各個構造函數中,“接受不定個數的實參”的版本被聲明爲explicit
:
namespace std{
template <typename... Types>
class tuple {
public:
explicit tuple(const Types&...);
template <typename... UTypes> explicit tuple(UTypes&&...);
...
};
}
這是爲了避免單一值被隱式轉換爲“帶一個元素的tuple
”:
template <typename... Args>
void foo(const std::tuple<Args...> t);
foo(42); // ERROR
foo(make_tuple(42)); // OK
這種情況下,不可以使用賦值語法將某個tuple
初始化,因爲那會被視爲一個隱式轉換:
std::tuple<int, double> t1(42, 3.14); // OK, 老的語法
std::tuple<int, double> t1{42, 3.14}; // OK, 新的語法
std::tuple<int, double> t1={42, 3.14}; // ERROR
對於tuple
,必須明確地將初值轉化爲一個tuple
(如運用make_tuple()
):
std::vector<std::tuple<int, float>> v{std::make_tuple(1,1.0),
std::make_tuple(2,2.0) }; //OK
std::tuple<int, int, int> foo() {
return std::make_tuple(1,2,3); // OK
}
std::tuple<int, int, int> foo() {
return {1,2,3}; // ERROR
}
4. 其它的tuple
特性
有些輔助函數是特別爲tuple
而設計的,特別是爲了支持泛型編程:
tuple_size<tupletype>::value
可獲得元素個數tuple_element<idx, tupletype>::type
可取得第idx個元素的類型(也就是get()
返回值的類型)tuple_cat()
可將多個tuple
串接成一個tuple
5. tuple
的輸入輸出
tuple
最初公開於Boost程序庫,在那,tuple
可以將其元素寫至output stream, 但C++標準庫並不支持這個性質。以下頭文件可以使用標準輸出操作符<<
打印任何tuple
:
// util/printtuple.hpp
#include <tuple>
#include <iostream>
// print element with index IDX of tuple with MAX elements
template <int IDX, int MAX, typename... Args>
struct PRINT_TUPLE {
static void print (std::ostream& strm, const std::tuple<Args...>& t){
strm << std::get<IDX>(t) << (IDX+1 == MAX ? "" : ",");
PRINT_TUPLE<IDX+!, MAX, Args...>::print(strm, t);
}
};
// partial specialization to end the recursion
template <int MAX, typename... Args>
struct PRINT_TUPLE<MAX,MAX,Args...> {
static void print(std::ostream& strm, const std::tuple<Args...>& t){
}
};
// output operator for tuples
template <typename... Args>
std::ostream& operator << (std:: ostream& strm, const std::tuple<Args...>& t){
strm << "[";
PRINT_TUPLE<0,sizeof...(Args),Args...>::print(strm,t);
return strm << "]";
}
這段代碼大量運用模板超編程,在編譯期就遞歸迭代tuple
的所有元素,每次調用PRINT_TUPLE<>::print()
就打印出一個元素,然後調用相同函數打印下一個元素。一個偏特化版本(其“當前索引IDX”和“tuple
內的元素個數MAX”相等)來終結遞歸調用。
6. tuple
和pair
轉換
可以用一個pair
作爲初值,初始化一個雙元素tuple
,也可以將一個pair
賦值給一個雙元素tuple
。;注意,pair<>
提供了一個特殊構造函數,以tuple
爲初值。