前言
C++中的元組(tuple)是一個(gè)類似于 pair 的標(biāo)準(zhǔn)庫(kù)模板類(template class),它是一個(gè)固定大小且可以包含不同類型的值的容器。
元組可以用于組合和處理多個(gè)值,類似于結(jié)構(gòu)體(struct),但不需要定義新的類型。元組的長(zhǎng)度和每個(gè)元素的類型都是在編譯時(shí)確定的。由于已經(jīng)在編譯期間已經(jīng)確定元組的大小,所以不能在后續(xù)擴(kuò)充 tuple 的大小了。
C++中的元組(tuple)是在C++11標(biāo)準(zhǔn)中引入的。在此之前,C++標(biāo)準(zhǔn)庫(kù)中沒有元組的實(shí)現(xiàn),但是一些第三方庫(kù)和框架提供了自己的元組實(shí)現(xiàn)。
C++11標(biāo)準(zhǔn)引入了std::tuple模板類,它定義了一個(gè)固定大小且可以包含不同類型的值的容器。std::tuple是一個(gè)非常有用的工具,可以用于組合和處理多個(gè)值,以及在函數(shù)返回多個(gè)值、遍歷多個(gè)容器和參數(shù)包展開等場(chǎng)景中使用。
除了std::tuple之外,C++11標(biāo)準(zhǔn)還引入了std::make_tuple和std::tie函數(shù),它們可以方便地創(chuàng)建和解包元組。C++11標(biāo)準(zhǔn)庫(kù)中還引入了一些其他的元組相關(guān)功能,如std::tuple_size和std::tuple_element等,用于獲取元組的大小和元素類型。
當(dāng)我們需要將一些數(shù)據(jù)組合成單一的對(duì)象,但又不想定義一個(gè)新的數(shù)據(jù)結(jié)構(gòu)來表示這些數(shù)據(jù)時(shí), tuple 便適用于此場(chǎng)景。
接下來的示例我都是參考chatgpt。
一、tuple的使用
1.1 基本使用
(1)
#include <tuple>
#include <string>
#include <iostream>
int main() {
//初始化 tuple :myTuple
//std::tuple<int, std::string, double> myTuple(42, "hello", 3.14);
std::tuple<int, std::string, double> myTuple;
//使用 make_tuple 初始化 tuple
myTuple = std::make_tuple(42, "hello", 3.14);
//tuple 成員都是未命名的,使用 std::get()函數(shù)訪問 tuple 成員:get<i>(tuple)
std::cout << std::get<0>(myTuple) << std::endl; // 42
std::cout << std::get<1>(myTuple) << std::endl; // "hello"
std::cout << std::get<2>(myTuple) << std::endl; // 3.14
//使用 tuple_size 獲取 tuple 中元素的個(gè)數(shù)
std::cout << std::tuple_size<decltype(myTuple)>::value << std::endl; //3
return 0;
}
上面的代碼創(chuàng)建了一個(gè)包含三個(gè)元素的元組,并使用get函數(shù)分別訪問每個(gè)元素的值。元組的元素可以使用索引或者std::get函數(shù)來訪問。
(2)
元組還可以使用std::tie函數(shù)將元素解包到不同的變量中:
#include <tuple>
#include <string>
#include <iostream>
int main() {
std::tuple<int, std::string, double> myTuple(42, "hello", 3.14);
std::cout << std::get<0>(myTuple) << std::endl; // 42
std::cout << std::get<1>(myTuple) << std::endl; // "hello"
std::cout << std::get<2>(myTuple) << std::endl; // 3.14
int myInt;
std::string myString;
double myDouble;
//使用 tie 將 myTuple這將元組的每個(gè)元素分別賦值給myInt、myString和myDouble變量。
std::tie(myInt, myString, myDouble) = myTuple;
std::cout << myInt << std::endl; // 42
std::cout << myString << std::endl; // "hello"
std::cout << myDouble << std::endl; // 3.14
return 0;
}
1.2 tuple_element
std::tuple_element是一個(gè)類型別名模板,用于獲取std::tuple中指定位置的元素類型。
#include <iostream>
#include <tuple>
#include <type_traits>
int main() {
// 創(chuàng)建一個(gè)std::tuple對(duì)象
std::tuple<int, double, std::string> myTuple(42, 3.14, "hello");
// 獲取元組的第一個(gè)元素
int x = std::get<0>(myTuple);
std::cout << "x = " << x << std::endl;
// 獲取元組的第二個(gè)元素
double y = std::get<1>(myTuple);
std::cout << "y = " << y << std::endl;
// 獲取元組的第三個(gè)元素
std::string z = std::get<2>(myTuple);
std::cout << "z = " << z << std::endl;
// 檢查元素類型
// std::tuple_element<0, decltype(myTuple)>::type表示獲取myTuple的第一個(gè)元素的類型,即int類型
std::cout << "type of 1st element: " << typeid(std::tuple_element<0, decltype(myTuple)>::type).name() << std::endl;
std::cout << "type of 2nd element: " << typeid(std::tuple_element<1, decltype(myTuple)>::type).name() << std::endl;
std::cout << "type of 3rd element: " << typeid(std::tuple_element<2, decltype(myTuple)>::type).name() << std::endl;
// 使用std::tuple_element_t類型別名獲取元素類型
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, int>);
static_assert(std::is_same_v<std::tuple_element_t<1, decltype(myTuple)>, double>);
static_assert(std::is_same_v<std::tuple_element_t<2, decltype(myTuple)>, std::string>);
return 0;
}
其中:
typeid(std::tuple_element<0, decltype(myTuple)>::type).name()
typeid是一個(gè)C++運(yùn)算符,用于獲取一個(gè)表達(dá)式的類型信息。當(dāng)應(yīng)用于一個(gè)類型時(shí),typeid返回一個(gè)std::type_info對(duì)象,表示該類型的類型信息。而std::type_info::name()函數(shù)用于獲取該類型的名稱。
在這段代碼中,typeid(std::tuple_element<0, decltype(myTuple)>::type)用于獲取myTuple的第一個(gè)元素的類型,即int類型的std::type_info對(duì)象。而std::type_info::name()函數(shù)用于獲取int類型的名稱,即字符串"int"。
類似地,typeid(std::tuple_element<1, decltype(myTuple)>::type).name()用于獲取myTuple的第二個(gè)元素的類型名稱,即字符串"double";而typeid(std::tuple_element<2, decltype(myTuple)>::type).name()用于獲取myTuple的第三個(gè)元素的類型名稱,即字符串"std::string"。
總之,typeid運(yùn)算符和std::type_info類是C++標(biāo)準(zhǔn)庫(kù)中用于獲取類型信息的工具,它們可以幫助我們?cè)谶\(yùn)行時(shí)查詢和操作類型信息。在程序中使用typeid運(yùn)算符和std::type_info類可以幫助我們進(jìn)行一些類型安全檢查和調(diào)試任務(wù)。
其中:
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, int>);
使用了C++11中的static_assert斷言,用于在編譯時(shí)檢查一個(gè)表達(dá)式是否為true,如果不是,則會(huì)在編譯時(shí)產(chǎn)生一個(gè)編譯錯(cuò)誤。
在這段代碼中,static_assert用于檢查std::tuple_element_t<0, decltype(myTuple)>是否等于int類型。其中,std::tuple_element_t<0, decltype(myTuple)>表示獲取myTuple的第一個(gè)元素的類型,即int類型別名。而std::is_same_v是一個(gè)類型比較模板,用于檢查兩個(gè)類型是否相同,如果相同則返回true,否則返回false。
因此,如果std::tuple_element_t<0, decltype(myTuple)>等于int類型,則static_assert不會(huì)產(chǎn)生編譯錯(cuò)誤,程序正常編譯和運(yùn)行;否則,static_assert會(huì)產(chǎn)生編譯錯(cuò)誤,提示用戶類型不匹配。
這種在編譯期間進(jìn)行靜態(tài)檢查的技術(shù)可以幫助我們?cè)诰幾g時(shí)發(fā)現(xiàn)代碼中的錯(cuò)誤,從而提高代碼的健壯性和可維護(hù)性。
比如我把上述改為:
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, long>);
$ g++ tuple.cpp
tuple.cpp: In function ‘int main()’:
tuple.cpp:28:24: error: static assertion failed
28 | static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, long>);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
編譯期間報(bào)錯(cuò)。
1.3 tuple引用的使用
tuple可以通過引用(reference)來訪問和修改元組中的元素。
#include <iostream>
#include <tuple>
#include <functional>
int main() {
int x = 42;
double y = 3.14;
// 創(chuàng)建一個(gè)std::tuple對(duì)象,存儲(chǔ)x和y的引用
std::tuple<std::reference_wrapper<int>, std::reference_wrapper<double>> myTuple(std::ref(x), std::ref(y));
// 修改元組中的元素
std::get<0>(myTuple).get() = 100;
std::get<1>(myTuple).get() = 2.718;
// 輸出原始變量的值
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
//修改x,y的值
x = 10;
y = 1.1;
//tuple元素中的值也發(fā)生了相應(yīng)的變換
std::cout << std::get<0>(myTuple) << std::endl; // 10
std::cout << std::get<1>(myTuple) << std::endl; // 1.1
return 0;
}
std::ref 是一個(gè)函數(shù)模板,它接受一個(gè)對(duì)象的引用,并返回一個(gè)包裝(wrapper)該引用的 std::reference_wrapper 對(duì)象。這樣,我們就可以將這個(gè) std::reference_wrapper 對(duì)象作為元素插入到 std::tuple 中。
首先創(chuàng)建了兩個(gè)變量x和y,然后創(chuàng)建了一個(gè)std::tuple對(duì)象myTuple,存儲(chǔ)了x和y的引用。在創(chuàng)建myTuple時(shí),我們使用了std::ref函數(shù)將x和y轉(zhuǎn)換為了std::reference_wrapper類型,即引用類型的包裝器。然后,我們使用std::get函數(shù)和std::reference_wrapper::get函數(shù)分別訪問和修改了myTuple中的元素。最后,我們輸出了原始變量x和y的值,可以看到它們已經(jīng)被修改為了std::tuple中的值。
需要注意的是,當(dāng)我們使用std::ref函數(shù)創(chuàng)建std::tuple時(shí),需要使用std::reference_wrapper類型來存儲(chǔ)引用,因?yàn)閟td::tuple通常不直接存儲(chǔ)引用類型。在訪問std::tuple中的元素時(shí),需要使用std::reference_wrapper::get函數(shù)來獲取原始的引用變量。
std::tuple可以存儲(chǔ)引用類型,但需要注意引用的生命周期問題。
當(dāng)我們將一個(gè)引用類型的變量作為std::tuple的元素時(shí),實(shí)際上是將該引用的值存儲(chǔ)在std::tuple中。如果該引用的原始變量在std::tuple被訪問之前被銷毀了,那么在訪問std::tuple時(shí)就會(huì)出現(xiàn)未定義行為。在訪問std::tuple中的引用元素時(shí),我們需要確保引用的原始變量的生命周期足夠長(zhǎng),以避免未定義行為。通常情況下,我們應(yīng)該避免在std::tuple中存儲(chǔ)引用類型,并使用std::reference_wrapper類型代替引用類型來存儲(chǔ)元素的引用。
總之,使用std::ref函數(shù)可以方便地將變量轉(zhuǎn)換為std::tuple的元素引用,從而方便地訪問和修改元素。同時(shí),使用std::reference_wrapper類型可以避免在std::tuple中存儲(chǔ)引用類型的問題。
二、c++17結(jié)構(gòu)化綁定
1.結(jié)構(gòu)化綁定簡(jiǎn)介
C++17引入了結(jié)構(gòu)化綁定(Structured Bindings)特性,它允許我們使用一種更簡(jiǎn)潔的語法來分解(解包)一個(gè)復(fù)雜類型的成員變量。結(jié)構(gòu)化綁定通過將一個(gè)復(fù)雜類型的成員變量分解為多個(gè)單獨(dú)的變量,使得我們可以更方便地訪問和處理這些變量。
原則上,結(jié)構(gòu)化綁定可以用于公有成員,原始C-style數(shù)組,以及“似若tuple”的對(duì)象(比如:std::pair,std::tuple和std::array):
下面是一個(gè)簡(jiǎn)單的示例:
#include <iostream>
#include <tuple>
//x,y是結(jié)構(gòu)體的公有成員
struct MyStruct {
int x;
double y;
};
int main() {
MyStruct myObj {42, 3.14};
// 使用結(jié)構(gòu)化綁定分解myObj成為單獨(dú)的變量x和y
auto [x, y] = myObj;
// 輸出分解后的變量
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
return 0;
}
結(jié)構(gòu)化綁定的好處是可以直接通過名字訪問值,并且由于名字可以傳遞語義信息,使得代碼可讀性也大大提高。
在上面的示例中,我們首先定義了一個(gè)結(jié)構(gòu)體MyStruct,其中包含兩個(gè)成員變量x和y。然后,我們創(chuàng)建了一個(gè)MyStruct對(duì)象myObj,包含了一些隨機(jī)的值。接著,我們使用結(jié)構(gòu)化綁定將myObj分解為兩個(gè)單獨(dú)的變量x和y。最后,我們輸出了分解后的變量x和y的值。
需要注意的是,結(jié)構(gòu)化綁定只能用于支持std::tuple-like接口的類型(例如std::pair和std::tuple),或者可以使用std::get函數(shù)進(jìn)行訪問的類型。如果我們想使用結(jié)構(gòu)化綁定來分解自定義的類型,需要在該類型中實(shí)現(xiàn)std::tuple-like接口或者提供std::get函數(shù)的實(shí)現(xiàn)。
總之,結(jié)構(gòu)化綁定是C++17引入的一項(xiàng)非常方便的特性,它可以幫助我們更方便地訪問和處理復(fù)雜類型的成員變量。
2.tuple 使用結(jié)構(gòu)化綁定
#include <iostream>
#include <tuple>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello, world!");
// 使用結(jié)構(gòu)化綁定分解myTuple成為單獨(dú)的變量x, y, z
auto [x, y, z] = myTuple;
// 輸出分解后的變量
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
std::cout << "z = " << z << std::endl;
return 0;
}
在上面的示例中,我們首先創(chuàng)建了一個(gè)std::tuple對(duì)象myTuple,包含了三個(gè)元素。然后,我們使用結(jié)構(gòu)化綁定將myTuple分解為三個(gè)單獨(dú)的變量x、y和z。最后,我們輸出了分解后的變量的值。
三、tuple返回多個(gè)值
使用std::tuple來返回多個(gè)值可以使得代碼更加簡(jiǎn)潔和易于理解。
例子:
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> myFunction() {
int x = 42;
double y = 3.14;
std::string z = "Hello, world!";
return std::make_tuple(x, y, z);
}
int main() {
// 調(diào)用myFunction函數(shù),獲取返回值
// 使用結(jié)構(gòu)化綁定特性
auto [x, y, z] = myFunction();
// 輸出返回值
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
std::cout << "z = " << z << std::endl;
return 0;
}
在上面的示例中,我們定義了一個(gè)函數(shù)myFunction,該函數(shù)返回一個(gè)std::tuple對(duì)象,包含了三個(gè)不同類型的值。然后,我們?cè)谥骱瘮?shù)中調(diào)用myFunction函數(shù),并使用結(jié)構(gòu)化綁定將返回值分解為三個(gè)單獨(dú)的變量x、y和z。最后,我們輸出了分解后的變量的值。
需要注意的是,使用std::tuple返回多個(gè)值可能會(huì)影響代碼的可讀性。因此,我們應(yīng)該在需要時(shí)選擇合適的返回類型,以使代碼更加清晰易懂。
總結(jié)
元組(tuple)的使用有其優(yōu)點(diǎn)和缺點(diǎn)。下面是一些主要的優(yōu)點(diǎn)和缺點(diǎn):
優(yōu)點(diǎn):
元組可以容納任意數(shù)量和類型的元素,因此它們非常靈活。這意味著您可以使用它們來存儲(chǔ)和處理多個(gè)值,而不必定義新的類型或容器。
元組可以通過std::get函數(shù)或使用類似于結(jié)構(gòu)體的點(diǎn)表示法來訪問其元素。這使得元組使用起來非常方便。
元組可以在函數(shù)中作為返回值,以便返回多個(gè)值。這使得函數(shù)的返回值更加清晰和易讀。
缺點(diǎn):
元組的元素可以通過std::get函數(shù)和索引訪問,但不能像結(jié)構(gòu)體一樣使用成員變量名。這使得代碼可讀性稍有降低。
元組的長(zhǎng)度和每個(gè)元素的類型都是在編譯時(shí)確定的。這意味著您無法在運(yùn)行時(shí)動(dòng)態(tài)添加或刪除元素。
元組的元素可以包含不同的類型,這意味著您需要小心處理類型轉(zhuǎn)換和類型匹配的問題。文章來源:http://www.zghlxwxcb.cn/news/detail-427795.html
參考資料
Chatgpt
https://github.com/CnTransGroup/Cpp17TheCompleteGuideChinese/blob/master/src/part1/cp1.md
https://www.zhihu.com/question/298981020文章來源地址http://www.zghlxwxcb.cn/news/detail-427795.html
到了這里,關(guān)于C++11 tuple的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!