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)