Namespaces
Variants

Pack (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Template specialization
Parameter packs (C++11)
Miscellaneous

パックは、以下のいずれかを定義するC++エンティティです:

  • パラメータパック
  • template parameter pack
  • function parameter pack
(C++20以降)
(C++26以降)

テンプレートパラメータパックは、ゼロ個以上のテンプレート引数(定数、型、またはテンプレート)を受け入れるテンプレートパラメータです。関数パラメータパックは、ゼロ個以上の関数引数を受け入れる関数パラメータです。

ラムダ初期化キャプチャパックは、その初期化子のパック展開における各要素に対して初期化キャプチャを導入するラムダキャプチャです。

(since C++20)

構造化バインディングパックは、ゼロ個以上の構造化バインディングを導入する構造化バインディング宣言内の識別子です。

(C++26以降)

パックの要素数は以下と等しい:

  • パラメータパックに提供された引数の数(パックがテンプレートまたは関数パラメータパックである場合)
  • 初期化子のパック展開における要素数(パックがラムダ init-capture パックの場合)
(C++20 以降)
  • 構造化バインディング初期化子のサイズから構造化バインディング宣言内の非パック要素の数を引いた値(パックが構造化バインディングパックである場合)
(C++26 以降)

少なくとも1つのパラメータパックを持つテンプレートは、 可変引数テンプレート と呼ばれます。

目次

構文

テンプレートパラメータパック( エイリアステンプレート クラステンプレート 変数テンプレート (C++14以降) コンセプト (C++20以降) および 関数テンプレート のパラメータリストに現れる)

... パック名  (オプション) (1)
typename | class ... パック名  (オプション) (2)
型制約 ... パック名  (オプション) (3) (C++20以降)
template < パラメータリスト > class ... パック名  (オプション) (4) (C++17まで)
template < パラメータリスト > typename | class ... パック名  (オプション) (4) (C++17以降)

関数パラメータパック( declarator の一種で、可変引数関数テンプレートの関数パラメータリストに現れる)

pack-name ... pack-param-name  (オプション) (5)

非パラメータパックの構文については、 lambda init-capture pack および structured binding pack (C++26以降) を参照してください。

(C++20以降)

パック展開(テンプレートの本体に現れる)

pattern ... (6)
1) 名前を省略可能な定数テンプレートパラメータパック
2) オプションの名前を持つ型テンプレートパラメータパック
3) オプションの名前付き 制約付き 型テンプレートパラメータパック
(C++20以降)
4) オプションの名前を持つテンプレートテンプレートパラメータパック
5) オプションの名前を持つ関数パラメータパック
6) パック展開: ゼロ個以上の pattern のリストに展開されます。パターンには少なくとも1つのパックが含まれている必要があります。

説明

可変引数クラステンプレートは任意の数のテンプレート引数でインスタンス化できます:

template<class... Types>
struct Tuple {};
Tuple<> t0;           // Typesには引数が含まれない
Tuple<int> t1;        // Typesには1つの引数が含まれる: int
Tuple<int, float> t2; // Typesには2つの引数が含まれる: intとfloat
Tuple<0> t3;          // エラー: 0は型ではない

可変引数関数テンプレートは任意の数の関数引数で呼び出すことができます(テンプレート引数は template argument deduction によって推論されます):

template<class... Types>
void f(Types... args);
f();       // OK: args には引数が含まれない
f(1);      // OK: args には1つの引数が含まれる: int
f(2, 1.0); // OK: args には2つの引数が含まれる: int と double

プライマリクラステンプレートでは、テンプレートパラメータパックはテンプレートパラメータリストの最終パラメータでなければなりません。関数テンプレートでは、後続のすべてのパラメータが関数引数から推論可能であるか、デフォルト引数を持つ場合、テンプレートパラメータパックはリスト内でより早く現れることができます:

template<typename U, typename... Ts>    // OK: Uの推論が可能
struct valid;
// template<typename... Ts, typename U> // エラー: Ts...が末尾にない
// struct Invalid;
template<typename... Ts, typename U, typename=void>
void valid(U, Ts...);    // OK: Uの推論が可能
// void valid(Ts..., U); // 使用不可: この位置ではTs...は非推論コンテキスト
valid(1.0, 1, 2, 3);     // OK: Uをdouble、Tsを{int, int, int}として推論

可変個引数テンプレートのすべての有効な特殊化が空のテンプレートパラメータパックを必要とする場合、プログラムは不適格(診断不要)です。

パック展開

省略記号に続くパターンで、少なくとも1つのパックの名前が少なくとも1回現れるものは、 展開 され、パックの名前がパック内の各要素で順次置き換えられた、ゼロ個以上のパターンのインスタンス化になります。 alignment specifiers のインスタンス化はスペースで区切られ、その他のインスタンス化はカンマで区切られます。

template<class... Us>
void f(Us... pargs) {}
template<class... Ts>
void g(Ts... args)
{
    f(&args...); // 「&args...」はパック展開
                 // 「&args」はそのパターン
}
g(1, 0.2, "a"); // Ts... args は int E1, double E2, const char* E3 に展開される
                // &args... は &E1, &E2, &E3 に展開される
                // Us... pargs は int* E1, double* E2, const char** E3 に展開される

2つのパックの名前が同じパターンに現れる場合、それらは同時に展開され、同じ長さでなければなりません:

template<typename...>
struct Tuple {};
template<typename T1, typename T2>
struct Pair {};
template<class... Args1>
struct zip
{
    template<class... Args2>
    struct with
    {
        typedef Tuple<Pair<Args1, Args2>...> type;
        // Pair<Args1, Args2>... はパック展開
        // Pair<Args1, Args2> はパターン
    };
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... は以下のように展開される
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 は Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
// typedef zip<short>::with<unsigned short, unsigned>::type T2;
// エラー: パック展開に異なる長さのパックが含まれています

パック展開が別のパック展開内にネストされている場合、最も内側のパック展開内に現れるパックはそれによって展開され、外側のパック展開内で言及されているが最も内側のものには含まれていない別のパックが存在しなければなりません:

template<class... Args>
void g(Args... args)
{
    f(const_cast<const Args*>(&args)...); 
    // const_cast<const Args*>(&args) はパターンであり、2つのパック
    // (Args と args) を同時に展開する
    f(h(args...) + args...); // ネストされたパック展開:
    // 内側のパック展開は "args..." であり、最初に展開される
    // 外側のパック展開は h(E1, E2, E3) + args... であり、次に展開される
    // (h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3 として)
}

パック内の要素数がゼロ(空パック)の場合、パック展開のインスタンス化は、たとえパック展開を完全に省略した場合に不正な形式となるか構文の曖昧性が生じるような状況であっても、包含する構文の解釈を変更しません。インスタンス化は空のリストを生成します。

template<class... Bases> 
struct X : Bases... { };
template<class... Args> 
void f(Args... args) 
{
    X<Args...> x(args...);
}
template void f<>(); // OK、X<>は基底クラスを持たない
                     // xは値初期化されたX<>型の変数

展開軌跡

展開が行われる場所に応じて、結果のカンマ区切り(または アライメント指定子 ではスペース区切り)のリストは、異なる種類のリストとなります:関数引数リスト、メンバ初期化子リスト、属性リストなど。以下はすべての許可されたコンテキストのリストです:

関数引数リスト

パック展開は関数呼び出し演算子の括弧内に現れることがあり、その場合、省略記号の左側にある最大の式または 波括弧で囲まれた初期化子リスト が展開されるパターンとなります:

f(args...);              // f(E1, E2, E3) に展開される
f(&args...);             // f(&E1, &E2, &E3) に展開される
f(n, ++args...);         // f(n, ++E1, ++E2, ++E3) に展開される
f(++args..., n);         // f(++E1, ++E2, ++E3, n) に展開される
f(const_cast<const Args*>(&args)...);
// f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))
f(h(args...) + args...); // 以下のように展開される
// f(h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)

括弧付き初期化子

パック展開は、 direct initializer の括弧内、 function-style cast およびその他の文脈( member initializer new-expression など)内に現れることがあります。その場合のルールは、前述の関数呼び出し式のルールと同一です:

Class c1(&args...);             // Class::Class(&E1, &E2, &E3) を呼び出す
Class c2 = Class(n, ++args...); // Class::Class(n, ++E1, ++E2, ++E3) を呼び出す
::new((void *)p) U(std::forward<Args>(args)...) // std::allocator::allocate

波括弧で囲まれた初期化子

波括弧で囲まれた初期化子リストでは、パック展開も同様に現れます:

template<typename... Ts>
void func(Ts... args)
{
    const int size = sizeof...(args) + 2;
    int res[size] = {1, args..., 2};
    // 初期化リストは順序を保証するため、パックの各要素に対して順序通りに関数を呼び出すために使用できる:
    int dummy[sizeof...(Ts)] = {(std::cout << args, 0)...};
}

テンプレート引数リスト

パック展開は、テンプレートが展開に一致するパラメータを持つ限り、テンプレート引数リストの任意の場所で使用できます:

template<class A, class B, class... C>
void func(A arg1, B arg2, C... arg3)
{
    container<A, B, C...> t1; // container<A, B, E1, E2, E3> に展開される
    container<C..., A, B> t2; // container<E1, E2, E3, A, B> に展開される
    container<A, C..., B> t3; // container<A, E1, E2, E3, B> に展開される
}

関数パラメータリスト

関数のパラメータリストにおいて、省略記号がパラメータ宣言内に現れる場合(それが関数パラメータパックを命名するかどうかに関わらず(例: Args ... args ))、そのパラメータ宣言はパターンとなります:

template<typename... Ts>
void f(Ts...) {}
f('a', 1); // Ts... は void f(char, int) に展開される
f(0.1);    // Ts... は void f(double) に展開される
template<typename... Ts, int... N>
void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n); // Ts (&...arr)[N] は 
                            // const char (&)[2], int(&)[1] に展開される

注意: パターン Ts (&...arr)[N] では、他のすべてのパック展開とは異なり、省略記号は最後の要素ではなく最も内側の要素です。

注記: Ts (&...)[N] は許可されていません。C++11の文法では括弧で囲まれた省略記号に名前が必要なためです: CWG issue 1488

テンプレートパラメータリスト

パック展開はテンプレートパラメータリスト内に現れることがあります:

template<typename... T>
struct value_holder
{
    template<T... Values> // 定数テンプレートパラメータリストに展開されます
    struct apply {};      // 例: <int, char, int(&)[5]>
};

基底指定子とメンバ初期化子リスト

パック展開は、 クラス宣言 における基底クラスのリストを指定することができます。 通常、これはコンストラクタがこれらの基底クラスのコンストラクタを呼び出すために、 メンバ初期化子リスト で パック展開を使用する必要があることも意味します:

template<class... Mixins>
class X : public Mixins...
{
public:
    X(const Mixins&... mixins) : Mixins(mixins)... {}
};

Lambdaキャプチャ

パック展開は lambda 式のキャプチャ節に現れることがあります:

template<class... Args>
void f(Args... args)
{
    auto lm = [&, args...] { return g(args...); };
    lm();
}

sizeof... 演算子

sizeof... 演算子もパック展開として分類されます:

template<class... Types>
struct count
{
    static const std::size_t value = sizeof...(Types);
};

動的例外仕様

動的例外仕様 における例外のリストは、パック展開も可能です:

template<class... X>
void func(int arg) throw(X...)
{
    // ... throw different Xs in different situations
}
(C++17まで)

アライメント指定子

pack展開は、キーワード alignas で使用される型リストと式リストの両方で許可されます。インスタンス化はスペースで区切られます:

template<class... T>
struct Align
{
    alignas(T...) unsigned char buffer[128];
};
Align<int, short> a; // 展開後のアライメント指定子は
                     // alignas(int) alignas(short)
                     // (間にカンマはない)

属性リスト

pack展開は、属性の仕様で許可されている場合、 attributes のリスト内で許可されます。例:

template<int... args>
[[vendor::attr(args)...]] void* f();

Fold式

fold式 において、パターンは未展開のパックを含まない完全な部分式です。

using宣言

using宣言 では、宣言子のリストに省略記号が現れることがあります。これはテンプレートパラメータパックから派生する際に有用です:

template<typename... bases>
struct X : bases...
{
    using bases::g...;
};
X<B, D> x; // OK: B::g and D::g introduced
(C++17以降)


Pack indexing

パックインデックス では、パック展開には展開されていないパックの後に省略記号と添字が続きます。パックインデックス式のパターンは identifier であり、パックインデックス指定子のパターンは typedef-name です。

consteval auto first_plus_last(auto... args)
{
    return args...[0] + args...[sizeof...(args) - 1];
}
static_assert(first_plus_last(5) == 10);
static_assert(first_plus_last(5, 4) == 9);
static_assert(first_plus_last(5, 6, 2) == 7);

Friend declarations

クラスの フレンド宣言 では、各型指定子の後に省略記号を付けることができます:

struct C {};
struct E { struct Nested; };
template<class... Ts>
class R
{
    friend Ts...;
};
template<class... Ts, class... Us>
class R<R<Ts...>, R<Us...>>
{
    friend Ts::Nested..., Us...;
};
R<C, E> rce;           // classes C and E are friends of R<C, E>
R<R<E>, R<C, int>> rr; // E::Nested and C are friends of R<R<E>, R<C, int>>

Fold expanded constraints

畳み込み展開された制約 では、パターンはその畳み込み展開された制約の制約です。

畳み込み展開された制約はインスタンス化されません。

(C++26以降)

注記

機能テスト マクロ 標準 機能
__cpp_variadic_templates 200704L (C++11) 可変引数テンプレート
__cpp_pack_indexing 202311L (C++26) パックインデックス

以下の例は、 std::printf と同様の関数を定義し、フォーマット文字列内の % 文字の各出現を値で置換します。

最初のオーバーロードは、フォーマット文字列のみが渡され、パラメータ展開がない場合に呼び出されます。

2番目のオーバーロードは、引数の先頭用に個別のテンプレートパラメータとパラメータパックを含みます。これにより、再帰呼び出しがパラメータの末尾のみを渡し、空になるまで継続することが可能になります。

Targs はテンプレートパラメータパックであり、 Fargs は関数パラメータパックです。

#include <iostream>
void tprintf(const char* format) // 基底関数
{
    std::cout << format;
{
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // 再帰的可変引数関数
{
    for (; *format != '\0'; format++)
    {
        if (*format == '%')
        {
            std::cout << value;
            tprintf(format + 1, Fargs...); // 再帰呼び出し
            return;
        }
        std::cout << *format;
    }
}
int main()
{
    tprintf("% world% %\n", "Hello", '!', 123);
}

出力:

Hello world! 123

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用バージョン 公開時の動作 正しい動作
CWG 1533 C++11 メンバーのメンバー初期化子でパック展開が発生する可能性があった 許可されない
CWG 2717 C++11 アライメント指定子のインスタンス化はカンマ区切りであった スペース区切りである

関連項目

Function template 関数のファミリーを定義する
Class template クラスのファミリーを定義する
sizeof... パック内の要素数を問い合わせる
C-style variadic function 可変個の引数を受け取る
Preprocessor macros 同様に可変個引数を取ることができる
Fold expression 二項演算子を使用してパックを畳み込む
Pack indexing 指定されたインデックスのパック要素にアクセスする