可変長テンプレート
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 67/104。
任意の型の任意個数のargumentsを受け取るfunctionが必要な場合はどうすればよいでしょうか?従来のテンプレートでは、必要な型パラメーターの個数を正確に指定する必要があります。可変長テンプレートを使うと、テンプレートが任意個数のテンプレート引数を受け取れるため、この問題を解決できます。
この構文では、...(省略記号)を使用してパラメータパックを作成します。
template <typename... Args>
void printAll(Args... args) {
// Args はテンプレートパラメータパックです
// args は関数パラメータパックです
}arguments を処理するには、通常、base case を伴う recursion を使用します。parameter pack は、残りがなくなるまで一度に 1 つの argument ずつ展開されます。
// 基本ケース: 引数が残っていない
void print() {
std::cout << std::endl;
}
// 再帰ケース: 最初の引数を処理し、残りで再帰する
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // 残りの引数を展開する
}
print(1, 3.14, "hello", 'x'); // 出力: 1 3.14 hello x明示的な再帰を使わずに、より簡単な操作を行うために fold expressions(C++17)も使用できます。
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // フォールド式: すべての引数を加算する
}
std::cout << sum(1, 2, 3, 4) << std::endl; // 出力: 10可変長テンプレートは、std::make_unique、std::tuple、std::function など、多くの標準ライブラリ機能を支えています。これらにより、引数の型や個数のあらゆる組み合わせに対応する、型安全な関数を実現できます。
チャレンジ
簡単任意個数の引数を持つメッセージを処理するために、可変長テンプレートを使用した柔軟なロギングシステムを構築しましょう。パラメータパックを使って、値の連結、引数の個数のカウント、フォーマット済み出力の表示を行うユーティリティを作成します。
コードを2つのファイルに分けて構成します。
Variadic.h:ここで可変長テンプレート関数を定義します。すべての引数をスペースで区切って表示し、その後に改行を出力する
printAll関数を作成します。改行だけを表示する Base case と、first argument、スペースを表示した後、remaining arguments で再帰する recursive case を使う recursive approach を使用します。渡された arguments の個数を返す
countArgs関数を作成します。sizeof...を使って parameter pack のサイズを取得します。すべての数値 arguments を加算して結果を返す
sum関数を作成します。+operator を使った fold expression を使用します。すべての数値 arguments を乗算して結果を返す
product関数を作成します。*operator を使った fold expression を使用します。main.cpp:4つの入力を読み取ります(それぞれ別の行に入力します)。- 整数
- double
- string
- 文字
次の操作を行って可変長テンプレートを実演します。
- 4つすべての入力を
printAllに渡して呼び出し、結果を表示する - 整数、double、文字を使って
countArgsを呼び出したときの arguments の個数を表示する:Argument count: <count> - 整数、double、値 10、値 5 の合計を計算し、次の形式で表示する:
Sum: <result> - 整数、値 2、値 3 の Product を計算し、次の形式で表示する:
Product: <result> - string だけを引数として
printAllを呼び出す(single argument) - 引数なしで
printAllを呼び出す(改行だけを表示する)
たとえば、入力が 5、2.5、Hello、X の場合:
5 2.5 Hello X
Argument count: 3
Sum: 22.5
Product: 30
Hello
printAll が、4つの arguments から zero まで、異なる型の任意個数の arguments をどのように処理するかに注目してください。sum 関数と product 関数は fold expressions を使って、明示的な再帰なしにすべての値を簡潔に組み合わせています。countArgs 関数は、sizeof... によってコンパイル時に pack のサイズを取得できることを示しています。
自分で試してみよう
#include <iostream>
#include <string>
#include "Variadic.h"
using namespace std;
int main() {
// 入力を読み取る
int intVal;
double doubleVal;
string strVal;
char charVal;
cin >> intVal;
cin >> doubleVal;
cin >> strVal;
cin >> charVal;
// TODO: 4つの入力すべてで printAll を呼び出す
// TODO: Print argument count using countArgs with intVal, doubleVal, and charVal
// Format: "Argument count: <count>"
// TODO: intVal、doubleVal、10、および 5 の合計を計算して出力する
// 形式: "Sum: <result>"
// TODO: intVal、2、および 3 の積を計算して出力する
// 形式: "Product: <result>"
// TODO: 文字列だけで printAll を呼び出す
// TODO: 引数なしで printAll を呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ