C++之tuple

  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");

建立和初始化一個元素類型分別爲intintconst char*tupletuple的元素類型可以是引用,如:

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. tuplepair轉換

  可以用一個pair作爲初值,初始化一個雙元素tuple,也可以將一個pair賦值給一個雙元素tuple。;注意,pair<>提供了一個特殊構造函數,以tuple爲初值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章