C++ での std::tuple クラスとそのメンバー関数

胡金庫 2023年10月12日
  1. C++ で std::make_tuple 関数を使って std::tuple オブジェクトを構築する
  2. C++ で関数テンプレートを使って任意のサイズのタプルを出力する
C++ での std::tuple クラスとそのメンバー関数

この記事では、C++ で std::tuple クラスとそのメンバー関数を使用するための複数のメソッドについて説明します。

C++ で std::make_tuple 関数を使って std::tuple オブジェクトを構築する

関数 std::tuple は、異種タイプの固定サイズのコレクションを実装する STL クラステンプレートです。一般に、タプルは要素の有限シーケンスを示すために数学で頻繁に使用されますが、プログラミング言語のさまざまなタイプの製品にも関連付けられています。

C++ には、同様のクラス std::pair があり、2つの異種オブジェクトのみを格納できます。std::tuple オブジェクト宣言は通常、タプルの構成元となるタイプと、それらにアクセスする順序を指定します。これらのタイプは、std::pair と同様のテンプレートパラメータとして指定されます。std::tuple コマンドは、さまざまなコンストラクターで初期化できます。それでも、この場合、std::make_tuple 関数を利用します。std::make_tuple プロセスは可変数の引数を取り、型を自動的に推測しようとします。この関数は、指定された値を含む std::tuple オブジェクトを返します。

次のサンプルプログラムでは、printTupleOfThree という名前の関数を実装します。この関数は、3つの値のタプルを出力します。このテンプレート関数は、異なる数の要素を持つタプルを出力できないことに注意してください。printTupleOfThree コマンドは、3つ以上の要素を持つタプルで呼び出すことができますが、出力されるのは最初の 3つだけです。したがって、異なるサイズのタプルに対してオーバーロードされた関数を定義することは非効率的であるため、異なるアプローチを使用して問題を解決する必要があります。

#include <iostream>
#include <tuple>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::tuple;
using std::vector;

template <typename Tuple>
void printTupleOfThree(Tuple t) {
  cout << "(" << std::get<0>(t) << ", " << std::get<1>(t) << ", "
       << std::get<2>(t) << ")" << endl;
}

int main() {
  std::vector<string> v1{"one", "two", "three", "four", "five", "six"};
  std::vector<int> v2 = {1, 2, 3, 4, 5, 6};
  std::vector<float> v3 = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

  auto t1 = std::make_tuple(v1[0], v1[1], v2[0]);
  auto t2 = std::make_tuple(v1[0], v1[1], v2[0], v3[0], v3[1]);
  printTupleOfThree(t1);
  printTupleOfThree(t2);

  return EXIT_SUCCESS;
}

出力:

(one, two, 1)
(one, two, 2)

C++ で関数テンプレートを使って任意のサイズのタプルを出力する

前のセクションで提起された問題の解決策は、可変個引数テンプレートと呼ばれます。後者は、可変数の引数を受け入れるテンプレートの機能です。printTuple は、TuplePrinter クラスの print メンバー関数を呼び出す可変個引数テンプレート関数であることに注意してください。後者は print と呼ばれる単一のメンバー関数を定義するクラステンプレートですが、TuplePrinter クラスには 2つの定義があることに注意してください。この実装は再帰的なテンプレートのインスタンス化と呼ばれ、テンプレートベースのメタプログラミングで知られている手法です。

メタプログラミングには、コンパイル時のポリモーフィズムを利用するメソッドが含まれます。これは、この記事で扱うことができるよりもはるかに大きなトピックです。テンプレート機能とそれらを使用したメタプログラミング手法の詳細な概要については、C++ Templates - The Complete Guideという本を参照するとよいでしょう。その結果、printTuple 関数がさまざまなサイズのタプルオブジェクトと互換性があり、それらのすべてのメンバーを cout ストリームに出力できます。

#include <iostream>
#include <tuple>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::tuple;
using std::vector;

template <class Tuple, std::size_t N>
struct TuplePrinter {
  static void print(const Tuple& t) {
    TuplePrinter<Tuple, N - 1>::print(t);
    std::cout << ", " << std::get<N - 1>(t);
  }
};

template <class Tuple>
struct TuplePrinter<Tuple, 1> {
  static void print(const Tuple& t) { std::cout << std::get<0>(t); }
};

template <class... Args>
void printTuple(const std::tuple<Args...>& t) {
  std::cout << "(";
  TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
  std::cout << ")" << endl;
}

int main() {
  std::vector<string> v1{"one", "two", "three", "four", "five", "six"};
  std::vector<int> v2 = {1, 2, 3, 4, 5, 6};
  std::vector<float> v3 = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

  auto t1 = std::make_tuple(v1[0], v1[1], v3[0]);
  auto t2 = std::make_tuple(v1[0], v1[1], v2[1], v3[0], v3[1]);
  printTuple(t1);
  printTuple(t2);

  return EXIT_SUCCESS;
}

出力:

(one, two, 1.1)
(one, two, 2, 1.1, 2.2)
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook