Menu
Coddy logo textTech

可変長テンプレート

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_uniquestd::tuplestd::function など、多くの標準ライブラリ機能を支えています。これらにより、引数の型や個数のあらゆる組み合わせに対応する、型安全な関数を実現できます。

challenge icon

チャレンジ

簡単

任意個数の引数を持つメッセージを処理するために、可変長テンプレートを使用した柔軟なロギングシステムを構築しましょう。パラメータパックを使って、値の連結、引数の個数のカウント、フォーマット済み出力の表示を行うユーティリティを作成します。

コードを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つの入力を読み取ります(それぞれ別の行に入力します)。
    1. 整数
    2. double
    3. string
    4. 文字

    次の操作を行って可変長テンプレートを実演します。

    1. 4つすべての入力を printAll に渡して呼び出し、結果を表示する
    2. 整数、double、文字を使って countArgs を呼び出したときの arguments の個数を表示する:Argument count: <count>
    3. 整数、double、値 10、値 5 の合計を計算し、次の形式で表示する:Sum: <result>
    4. 整数、値 2、値 3 の Product を計算し、次の形式で表示する:Product: <result>
    5. string だけを引数として printAll を呼び出す(single argument)
    6. 引数なしで printAll を呼び出す(改行だけを表示する)

たとえば、入力が 52.5HelloX の場合:

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;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ