tuple c++


C++11: std::tuple | De C++ et alias OOPscenitates (oopscenities.net)

tuple is a C++11 construction and it is built heavily on variadic templates.

tuple is a variadic class template that stores an unlimited set of values of different types, defined when instantiating the tuple; for example:

tuple<int, int> x;

will store 2 integers.

tuple<string, int, bool> y;

will store one string, one integer and one boolean and so on.

 

Tuples are useful for several things:

  • They are light replacements for structures.
  • They can be useful to return several values from a function
  • They let you perform comparisons through all the value set

You can instantiate tuples in these ways:

tuple<int, int, bool> x; //instantiating but not initializing
tuple<int, string> y { 2, "hello" }; //instantiating AND initializing
auto z = make_tuple(2, 3, 4, "bye"s); //instantiating AND initializing throught the make_tuple helper function.

Consider you declare something like this:

auto xx = make_tuple(3.14, "PI"s);

To get the tuple values, you must to use the get<int>() function like in this code excerpt:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <tuple>
#include <iostream>
#include <string>
using namespace std;
 
int main()
{
    auto xx = make_tuple(3.14, "PI" s);
    auto & first = get<0>(xx);
    auto & second = get<1>(xx);
    cout << "(" << first << "; " << second << ")" << endl;
    return 0;
}

 

The template parameter is the index of the element to be retrieved, so, if the first element was an integer, the index 0 will retrieve the integer stored as the first value in the tuple, the index 0 will retrieve the second one and so on.

Tuples are also very useful to create structs “on the fly” and to use them in data structures; for example:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
   vector<tuple< int , string>> vec;
   vec.push_back(make_tuple(10, "ten" ));
   vec.emplace_back(20, "twenty" );
   vec.emplace_back(30, "thirty" );
 
   for ( auto & i : vec)
   {
      cout << "(" << get<0>(i) << "; " << get<1>(i) << ")" << endl;
   }
}

In this case I did not need to create a new struct in order to store the number and the number name, for example.

Other nice thing on tuples is that you can compare tuples relying on the comparison operators of the data types inside the tuples.

Look at this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <set>
#include <string>
#include <tuple>
 
using namespace std;
 
using car = tuple<string, string, int >;
 
void print( const car& c)
{
     cout << get<0>(c) << ", " << get<1>(c) << "; " << get<2>(c) << endl;
}
 
int main()
{
   set<car> cars;
   cars.emplace( "Toyota" , "Rav4" , 2012);
   cars.emplace( "VW" , "Jetta" , 2015);
   cars.emplace( "Chevrolet" , "Sonic" , 2013);
   cars.emplace( "BMW" , "X5" , 2014);
   cars.emplace( "VW" , "Jetta" , 2014);
   
   for ( auto & i : cars)
     print(i);
 
   cout << "******" << endl;
   auto it = cars.find(car { "Toyota" , "Rav4" , 2012 });
   if (it == cars.end())
     cerr << "CAR NOT FOUND" << endl;
   else
     print(*it);
     
   return 0;
}

When it is executed, this thing is displayed:

BMW, X5; 2014
Chevrolet, Sonic; 2013
Toyota, Rav4; 2012
VW, Jetta; 2014
VW, Jetta; 2015
******
Toyota, Rav4; 2012

The code and its execution contain several interesting things:

  • I defined an alias to a tuple called “car”, having it, I was able to use the tuple as a declared type in a lot of cases… without declaring it!
  • If I would have created a “car” struct, to use it inside a set, I would have to overload the operator<() for the struct, because the sets use this operator in order to insert the objects accordingly.
  • When executing the program, all the objects where ordered by brand, by model and by year; using the tuple operator<(). If the first element in a tuple A is less than the first element in a tuple B, the operator returns TRUE. If the first element in tuple A is greater than the first element in tuple B, the operator returns FALSE; but if both elements are equal, the comparison is performed between the second elements and so on.
  • Notice I created a car to pass it into the method find; I created it in exactly the same way I would have created a car struct instance.
  • Notice the element is found in the set (because operator<() too).

Now I want to modify my set in order to store the elements ordered by year, by brand and by model. Look at the new implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <set>
#include <string>
#include <tuple>
 
using namespace std;
 
using car = tuple<string, string, int >;
 
void print( const car& c)
{
     cout << get<0>(c) << ", " << get<1>(c) << "; " << get<2>(c) << endl;
}
 
int main()
{
   auto cc = []( auto & a, auto & b)
   {
     return tie(get<2>(a), get<0>(a), get<1>(a)) < tie(get<2>(b), get<0>(b), get<1>(b));
   };
    
   set<car, decltype (cc)> cars { cc };
   cars.emplace( "Toyota" , "Rav4" , 2012);
   cars.emplace( "VW" , "Jetta" , 2015);
   cars.emplace( "Chevrolet" , "Sonic" , 2013);
   cars.emplace( "BMW" , "X5" , 2014);
   cars.emplace( "VW" , "Jetta" , 2014);
   
   for ( auto & i : cars)
     print(i);
 
   cout << "******" << endl;
   auto it = cars.find(car { "Toyota" , "Rav4" , 2012 });
   if (it == cars.end())
     cerr << "CAR NOT FOUND" << endl;
   else
     print(*it);
     
   return 0;
}

The output is:

Toyota, Rav4; 2012
Chevrolet, Sonic; 2013
BMW, X5; 2014
VW, Jetta; 2014
VW, Jetta; 2015
******
Toyota, Rav4; 2012

The elements are ordered first by year.

Notice my “cc” lambda expression. It uses a function called “tie”.

“tie” is a function that takes a set of references and creates a tuple containing references (not values or copies) to the original values. So, tie is useful to create temporary light tuples. In my example, using “tie”, I was able to create other set of tuples with different logical order, so the tuple operator<() algorithm worked using this new logical order.

 

1,元組簡介

tuple是一個固定大小的不同類型值的集合,是泛化的std::pair。我們也可以把他當做一個通用的結構體來用,不需要創建結構體又獲取結構體的特征,在某些情況下可以取代結構體使程序更簡潔,直觀。std::tuple理論上可以有無數個任意類型的成員變量,而std::pair只能是2個成員,因此在需要保存3個及以上的數據時就需要使用tuple元組了。

tuple(元組)在c++11中開始引用的。tuple看似簡單,其實它是簡約而不簡單,可以說它是c++11中一個既簡單又復雜的東東,關於它簡單的一面是它很容易使用,復雜的一面是它內部隱藏了太多細節,要揭開它神秘的面紗時又比較困難。

2,tuple的創建和初始化


std::tuple<T1, T2, TN> t1; //創建一個空的tuple對象(使用默認構造),它對應的元素分別是T1和T2...Tn類型,采用值初始化。
std::tuple<T1, T2, TN> t2(v1, v2, ... TN); //創建一個tuple對象,它的兩個元素分別是T1和T2 ...Tn類型; 要獲取元素的值需要通過tuple的成員get<Ith>(obj)進行獲取(Ith是指獲取在tuple中的第幾個元素,請看后面具體實例)。
std::tuple<T1&> t3(ref&); // tuple的元素類型可以是一個引用
std::make_tuple(v1, v2); // 像pair一樣也可以通過make_tuple進行創建一個tuple對象
tuple的元素類型為引用:

std::string name;
std::tuple<string &, int> tpRef(name, 30);
// 對tpRef第一個元素賦值,同時name也被賦值 - 引用
std::get<0>(tpRef) = "Sven";

// name輸出也是Sven
std::cout << "name: " << name << '\n';
3,有關tuple元素的操作

等價結構體

開篇講過在某些時候tuple可以等同於結構體一樣使用,這樣既方便又快捷。如:

struct person {
char *m_name;
char *m_addr;
int *m_ages;
};

//可以用tuple來表示這樣的一個結構類型,作用是一樣的。
std::tuple<const char *, const char *, int>
2. 如何獲取tuple元素個數

當有一個tuple對象但不知道有多少元素可以通過如下查詢:

// tuple_size
#include <iostream> // std::cout
#include <tuple> // std::tuple, std::tuple_size

int main ()
{
std::tuple<int, char, double> mytuple (10, 'a', 3.14);

std::cout << "mytuple has ";
std::cout << std::tuple_size<decltype(mytuple)>::value;
std::cout << " elements." << '\n';

return 0;
}

//輸出結果:
mytuple has 3 elements
3.如何獲取元素的值

獲取tuple對象元素的值可以通過get<Ith>(obj)方法進行獲取;

Ith - 是想獲取的元素在tuple對象中的位置。

obj - 是想獲取tuple的對象

// tuple_size
#include <iostream> // std::cout
#include <tuple> // std::tuple, std::tuple_size

int main ()
{
std::tuple<int, char, double> mytuple (10, 'a', 3.14);

std::cout << "mytuple has ";
std::cout << std::tuple_size<decltype(mytuple)>::value;
std::cout << " elements." << '\n';

//獲取元素
std::cout << "the elements is: ";
std::cout << std::get<0>(mytuple) << " ";
std::cout << std::get<1>(mytuple) << " ";
std::cout << std::get<2>(mytuple) << " ";

std::cout << '\n';

return 0;
}

//輸出結果:
mytuple has 3 elements.
the elements is: 10 a 3.14
tuple不支持迭代,只能通過元素索引(或tie解包)進行獲取元素的值。但是給定的索引必須是在編譯器就已經給定,不能在運行期進行動態傳遞,否則將發生編譯錯誤:

for(int i=0; i<3; i++)
std::cout << std::get<i>(mytuple) << " "; //將引發編譯錯誤
4.獲取元素的類型

要想得到元素類型可以通過tuple_element方法獲取,如有以下元組對象:

std::tuple<std::string, int> tp("Sven", 20);

// 得到第二個元素類型

std::tuple_element<1, decltype(tp)>::type ages; // ages就為int類型

ages = std::get<1>(tp);

std::cout << "ages: " << ages << '\n';

//輸出結果:
ages: 20
5.利用tie進行解包元素的值

如同pair一樣也是可以通過tie進行解包tuple的各個元素的值。如下tuple對象有4個元素,通過tie解包將會把這4個元素的值分別賦值給tie提供的4個變量中。

#include <iostream>
#include <tuple>
#include <utility>

int main(int argc, char **argv) {
std::tuple<std::string, int, std::string, int> tp;
tp = std::make_tuple("Sven", 25, "Shanghai", 21);

// 定義接收變量
std::string name;
std::string addr;
int ages;
int areaCode;

std::tie(name, ages, addr, areaCode) = tp;
std::cout << "Output: " << '\n';
std::cout << "name: " << name <<", ";
std::cout << "addr: " << addr << ", ";
std::cout << "ages: " << ages << ", ";
std::cout << "areaCode: " << areaCode << '\n';

return 0;
}

//輸出結果:
Output:
name: Sven, addr: Shanghai, ages: 25, areaCode: 21
但有時候tuple包含的多個元素時只需要其中的一個或兩個元素,如此可以通過std::ignore進行變量占位,這樣將會忽略提取對應的元素。可以修改上述例程:

#include <iostream>
#include <tuple>
#include <utility>

int main(int argc, char **argv) {
std::tuple<std::string, int, std::string, int> tp;
tp = std::make_tuple("Sven", 25, "Shanghai", 21);

// 定義接收變量
std::string name;
std::string addr;
int ages;
int areaCode = 110;

std::tie(name, ages, std::ignore, std::ignore) = tp;
std::cout << "Output: " << '\n';
std::cout << "name: " << name <<", ";
std::cout << "addr: " << addr << ", ";
std::cout << "ages: " << ages << ", ";
std::cout << "areaCode: " << areaCode << '\n';

return 0;
}

//輸出結果:
Output:
name: Sven, addr: , ages: 25, areaCode: 110
6. tuple元素的引用

前面已經列舉了將引用作為tuple的元素類型。下面通過引用搭配make_tuple()可以提取tuple的元素值,將某些變量值設給它們,並通過改變這些變量來改變tuple元素的值:

#include <iostream>
#include <tuple>
#include <functional>

int main(int argc, char **agrv) {

std::tuple<std::string, int, float> tp1("Sven Cheng", 77, 66.1);

std::string name;
int weight;
float f;

auto tp2 = std::make_tuple(std::ref(name), std::ref(weight), std::ref(f)) = tp1;

std::cout << "Before change: " << '\n';
std::cout << "name: " << name << ", ";
std::cout << "weight: " << weight << ", ";
std::cout << "f: " << f << '\n';

name = "Sven";
weight = 80;
f = 3.14;

std::cout << "After change: " << '\n';
std::cout << "element 1st: " << std::get<0>(tp2) << ", ";
std::cout << "element 2nd: " << std::get<1>(tp2) << ", ";
std::cout << "element 3rd: " << std::get<2>(tp2) << '\n';

return 0;
}

//輸出結果:
Before change:
name: Sven Cheng, weight: 77, f: 66.1
After change:
element 1st: Sven, element 2nd: 80, element 3rd: 3.14


 


————————————————
版權聲明:本文為CSDN博主「sevencheng798」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sevenjoin/article/details/88420885


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM