1. 程式人生 > >C++:std::tuple使用說明

C++:std::tuple使用說明

目錄

一、如何建立std::tuple

二、std::tuple中的元素是在堆中建立的還是在棧中建立的

三、如何獲取std::tuple中存放的元素個數

四、如何獲取std::tuple中存放的元素型別

五、如何遍歷std::tuple中的元素

六、如何通過已有的std::tuple生成新的std::tuple



一、如何建立std::tuple

主要有如下4種方式:

  • std::tuple<>()
  • std::forward_as_tuple()
  • std::make_tuple()
  • std::tie()
#include <iostream>
#include <tuple>
#include <string>

using namespace std;

static int cnt = 0;

class Person {

private:
    int id; 

public:
    string name;
    int age;

    Person()  {
        id = ++cnt;
        cout << "Person()" << id <<endl;
    }

    Person(string _name, int _age): name(_name), age(_age) {
        id = ++cnt;
        cout << "Person(string _name, int _age)" << id << endl;
    }

    Person(const Person&) {
        id = ++cnt;
        cout << "Person(const Person&)" << id << endl;
    }

    Person(Person&&) {
        id = ++cnt;
        cout << "Person(Person&&)" << id << endl;
    }

    Person& operator=(const Person&) {
        cout << "operator=(const Person&)" << id << endl;

        return *this;
    }

};

int main () {
  {
    cout << "--------------------------------//(1)" << endl;
    tuple<int, bool, string, Person> t1 = tuple<int, bool, string, Person>(11, true, "ok", Person("ok", 11));
  }
  {
    cout << "--------------------------------//(2)" << endl;
    Person p("ok", 11);
    tuple<int, bool, string, Person> t2 = tuple<int, bool, string, Person>(11, true, "ok", p);
  }
  {
    cout << "--------------------------------//(3)" << endl;
    tuple<int, bool, string, Person> t3 = std::forward_as_tuple(11, true, "ok", Person("ok", 11));
  }
  {
    cout << "--------------------------------//(4)" << endl;
    Person p("ok", 11);
    tuple<int, bool, string, Person> t4 = std::forward_as_tuple(11, true, "ok", p);
  }
  {
    cout << "--------------------------------//(5)" << endl;
    tuple<int, bool, string, Person> t5 = std::make_tuple(11, true, "ok", Person("ok", 11));
  }
  {
    cout << "--------------------------------//(6)" << endl;
    Person p("ok", 11);
    tuple<int, bool, string, Person> t6 = std::make_tuple(11, true, "ok", p);
  }
    {
        cout << "--------------------------------//(7)" << endl;
        int i = 11;
        bool b = true;
        string s = "ok";
        Person p("ok", 11);
        tuple<int, bool, string, Person> t7 = std::tie(i, b, s, p);
    }
  
  return 1;
}

 執行結果如下:

--------------------------------//(1)
Person(string _name, int _age)1
Person(Person&&)2
--------------------------------//(2)
Person(string _name, int _age)3
Person(const Person&)4
--------------------------------//(3)
Person(string _name, int _age)5
Person(Person&&)6
--------------------------------//(4)
Person(string _name, int _age)7
Person(const Person&)8
--------------------------------//(5)
Person(string _name, int _age)9
Person(Person&&)10
Person(Person&&)11
--------------------------------//(6)
Person(string _name, int _age)12
Person(const Person&)13
Person(Person&&)14
--------------------------------//(7)
Person(string _name, int _age)15
Person(const Person&)16

 從執行結果可以看到,(1)(3)的效率最高,(2)(4)(7)次之,(5)更低,(6)最差。也就是說,儘量使用std::tuple<>()和std::forward_as_tuple()通過內部元素使用右值引用構造的形式;儘量不要使用std::make_tuple()的任何形式。

二、std::tuple中的元素是在堆中建立的還是在棧中建立的

std::tuple中的元素都是在棧中建立的

三、如何獲取std::tuple中存放的元素個數

template<typename Tuple>
int getSize() {
    return std::tuple_size<Tuple>::value;
};

四、如何獲取std::tuple中存放的元素型別

template<typename Tuple, int N>
struct TypeGetter
{
    using type = typename std::tuple_element<N, Tuple>::type;
};

N表示tuple中的第N個元素

五、如何遍歷std::tuple中的元素

#include <iostream>
#include <tuple>
#include <string>

using namespace std;

template<typename Tuple, int N = std::tuple_size<Tuple>::value>
struct Printer
{
  static void log(Tuple& t) {
    Printer<Tuple, N - 1>::log(t);

    using type = typename std::tuple_element<N - 1, Tuple>::type;
    std::string ts = typeid(type).name();
    type& v = std::get<N - 1>(t);

    std::cout << ts << ":" << v << std::endl;
  }
};

template<typename Tuple>
struct Printer<Tuple,  1>
{
  static void log(Tuple& t) {
    using type = typename std::tuple_element<0, Tuple>::type;
    std::string ts = typeid(type).name();
    type& v = std::get<0>(t);

    std::cout << ts << ":" << v << std::endl;
  }
};

int main() {
  std::tuple<int, bool, string> t = std::forward_as_tuple(11, true, "ok");
  Printer<std::tuple<int, bool, string>>::log(t);

  return 1;
}

 執行結果:

int:11
bool:1
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >:ok

 上述程式碼通過定義結構體模板Printer,並對Printer做偏特化實現對tuple物件的遍歷的。

六、如何通過已有的std::tuple生成新的std::tuple

主要有以下4種形式:

  • 拷貝
  • 轉移
  • 交換
  • 函式返回
tuple<int, bool, string, Person> swapTest() {
  {
    cout << "--------------------------------//(1)" << endl;
    tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
    cout << "--------------------------------" << endl;
    tuple<int, bool, string, Person> t1 = t0;  // equal to  tuple<int, bool, string, Person> t1(t0);
  }
  {
    cout << "--------------------------------//(2)" << endl;
    tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
    cout << "--------------------------------" << endl;
    tuple<int, bool, string, Person> t1 = move(t0);  // equal to  tuple<int, bool, string, Person> t1(move(t0));
  }
  {
    cout << "--------------------------------//(3)" << endl;
    tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
    cout << "--------------------------------" << endl;
    tuple<int, bool, string, Person> t1;
    cout << "--------------------------------" << endl;
    t1.swap(t0);   //  equal to  std::swap(t1, t0);    
  }

  cout << "--------------------------------//(4)" << endl;
  tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
  cout << "--------------------------------" << endl;

  return t0;
}

int main() {
  tuple<int, bool, string, Person> t = swapTest();
  cout << "--------------------------------" << endl;
  return 1;
}

執行結果:

----------------------------------------------------------------//(1)
Person(string _name, int _age)1
Person(Person&&)2
--------------------------------
Person(const Person&)3
----------------------------------------------------------------//(2)
Person(string _name, int _age)4
Person(Person&&)5
--------------------------------
Person(Person&&)6
----------------------------------------------------------------//(3)
Person(string _name, int _age)7
Person(Person&&)8
--------------------------------
Person()9
--------------------------------
Person(Person&&)10
operator=(const Person&)9
operator=(const Person&)8
----------------------------------------------------------------//(4)
Person(string _name, int _age)11
Person(Person&&)12
--------------------------------
Person(Person&&)13
--------------------------------

 從執行結果中可以看到,(2)(4)效率最高,元素都是採用轉移建構函式建立的,(1)次之,(3)最差。當然(3)效能最差是有原因,它畢竟是交換,需要雙向賦值。