C++教科書/標準ライブラリ編/<cstdarg>の章

編集

はじめに

編集

C++の標準ライブラリには、可変長引数をサポートする <cstdarg> ヘッダーが用意されています。このヘッダーは、主に関数に対して可変長の引数を渡す際に使用されます。可変長引数を扱うための型やマクロが定義されています。

va_listの説明

編集

va_list 型は、可変長引数のリストを表現する型です。この型のオブジェクトは、va_startva_argva_endva_copy マクロで操作されます。

va_start マクロ

編集

va_start マクロは、可変長引数のリストの処理を開始する際に呼び出します。以下は使用例です。

#include <cstdarg>

void printInts(int count, ...) {
    va_list args;
    va_start(args, count); // args を初期化
    for (int i = 0; i < count; i++) {
        int value = va_arg(args, int); // 次の引数を int 型として取得
        printf("%d ", value);
    }
    va_end(args); // args の処理を終了
}

int main() {
    printInts(5, 1, 2, 3, 4, 5); // 出力: 1 2 3 4 5
    return 0;
}

va_arg マクロ

編集

va_arg マクロは、可変長引数リストから次の引数を取得します。第2引数で型を指定する必要があります。

double average(int count, ...) {
    va_list args;
    va_start(args, count);
    double sum = 0;
    for (int i = 0; i < count; i++) {
        sum += va_arg(args, double); // 次の引数を double 型として取得
    }
    va_end(args);
    return sum / count;
}

va_copy マクロ (C++11から)

編集

va_copy マクロは、可変長引数リストのコピーを作成します (C++11から利用可能)。

#include <cstdarg>

double sum(int count, ...) {
    va_list args, args_copy;
    va_start(args, count);
    va_copy(args_copy, args); // args_copy を args のコピーとして初期化
    double result = 0;
    for (int i = 0; i < count; i++) {
        result += va_arg(args_copy, double);
    }
    va_end(args_copy);
    va_end(args);
    return result;
}

va_end マクロ

編集

va_end マクロは、可変長引数リストの処理を終了する際に呼び出します。

可変長引数関数の実装例

編集

可変長引数を使った関数の具体例を示します。

シンプルな例
#include <cstdarg>
#include <iostream>

int sum(int count, ...) {
    int result = 0;
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        result += va_arg(args, int);
    }
    va_end(args);
    return result;
}

int main() {
    std::cout << sum(3, 1, 2, 3) << std::endl;  // 6
    std::cout << sum(5, 4, 8, 3, 7, 1) << std::endl; // 23
    return 0;
}
応用的な例
#include <cstdarg>
#include <iostream>
#include <string>

std::string concatenate(const std::string& first, ...) {
    std::string result = first;
    va_list args;
    va_start(args, first);
    const char* str = va_arg(args, const char*);
    while (str != nullptr) {
        result += str;
        str = va_arg(args, const char*);
    }
    va_end(args);
    return result;
}

int main() {
    std::cout << concatenate("Hello", ", ", "world", "!") << std::endl; // Hello, world!
    std::cout << concatenate("C++", " ", "is", " ", "awesome", nullptr) << std::endl; // C++ is awesome
    return 0;
}

可変長引数の安全性と注意点

編集

可変長引数は型安全性が欠如しているため、引数の型が合っていないと未定義動作が発生する可能性があります。また、引数の数を正しく管理する必要があります。

可変長引数は、使い勝手の良さと柔軟性を持つ反面、安全性に欠けるトレードオフがあります。C++11から導入された可変長テンプレートは、可変長引数に比べてより型安全で扱いやすい機能です。

まとめ

編集

<cstdarg> ヘッダーでは、可変長引数をサポートするための va_list 型と va_startva_argva_copyva_end マクロが提供されています。可変長引数は柔軟性が高い一方で、型安全性が低いというトレードオフがあります。C++11以降では、可変長テンプレートが可変長引数に比べてより安全で便利な機能として導入されています。また、std::initilize_listの使用も検討に値します。