Namespaces
Variants

Pack indexing (since C++26)

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
Miscellaneous

指定されたインデックスの pack の要素にアクセスします。

目次

構文

id-expression ...[ expression ] (1)
typedef-name ...[ expression ] (2)
1) パックインデックス式
2) パックインデックス指定子
typedef-name - パックを名前付けする 識別子 または simple-template-id
id-expression - パックを名前付けする id-expression
expression - パックインデックス指定において、あるパック P に対して範囲 [ 0 , sizeof... ( P ) ) 内のインデックスとして指定される、 std:: size_t 型の 変換済み定数式 I

説明

パックインデックスは、未展開パックの パック展開 に続いて、添字内の省略記号とインデックスで構成されます。パックインデックスには2種類あります:パックインデックス式とパックインデックス指定子。

P を非空のパックとし、 P 0 , P 1 , ..., P n-1 を含むものとする。 I を有効なインデックスとすると、展開 P...[I] のインスタンス化は、パック P の要素 P I を生成する。

パックのインデックス指定に非定数式インデックス I は許可されていません。

int runtime_idx();
void bar(auto... args)
{
    auto a = args...[0];
    const int n = 1;
    auto b = args...[n];
    int m = 2;
    auto c = args...[m]; // エラー: 'm' は定数式ではありません
    auto d = args...[runtime_idx()]; // エラー: 'runtime_idx()' は定数式ではありません
}

テンプレートテンプレートパラメータのパックに対するインデックス指定は不可能です。

template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // エラー: 'Temps' はテンプレートテンプレートパラメータのパックです
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // エラー: 'Temps<>' はパック名を表していません
                         // 単純テンプレートIDではありますが

パックインデックス式

id-expression ...[ expression ]

パックインデックス式は id-expression を表し、パック要素 P I の式を示します。 id-expression は以下の宣言によって導入されるものとします:

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
    // 関数パラメータパック宣言で導入された 'args'
    return args...[I];
}
static_assert(element_at<0>(3, 5, 9) == 3);
static_assert(element_at<2>(3, 5, 9) == 9);
static_assert(element_at<3>(3, 5, 9) == 4); // エラー: 範囲外
static_assert(element_at<0>() == 1); // エラー: 範囲外、空のパック
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
    auto [...elems] = tup;
    // 構造化バインディングパック宣言で導入された 'elems'
    return elems...[I];
}
struct A { bool a; int b; };
static_assert(structured_binding_element_at<0>(A {true, 4}) == true);
static_assert(structured_binding_element_at<1>(A {true, 4}) == 4);
// 定数テンプレートパラメータパック宣言で導入された 'Vals'
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t double_at = Vals...[I] * 2; // OK
template <std::size_t I, typename... Args>
constexpr auto foo(Args... args)
{
    return [...members = args](Args...[I] op)
    {
        // ラムダ初期化キャプチャパックで導入された 'members'
        return members...[I] + op;
    };
}
static_assert(foo<0>(4, "Hello", true)(5) == 9);
static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");

id-expression以外の複雑な式のパックインデックスは許可されていません。

template <std::size_t I, auto... Vals>
constexpr auto identity_at = (Vals)...[I]; // エラー
// 代わりに 'Vals...[I]' を使用
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t triple_at = (Vals * 3)...[I]; // エラー
// 代わりに 'Vals...[I] * 3' を使用
template <std::size_t I, typename... Args>
constexpr decltype(auto) get(Args&&... args) noexcept
{
    return std::forward<Args>(args)...[I]; // エラー
    // 代わりに 'std::forward<Args...[I]>(args...[I])' を使用
}

decltype をパックインデックス式に適用することは、 decltype をid式に適用する場合と同じです。

void f() 
{
    [](auto... args)
    {
        using T0 = decltype(args...[0]);   // 'T0' は 'double' 型
        using T1 = decltype((args...[0])); // 'T1' は 'double&' 型
    }(3.14);
}
翻訳のポイント: - HTMLタグ、属性、C++コードはそのまま保持 - コメント部分のみ日本語に翻訳 - C++用語(`void`, `auto`, `decltype`, `double`, `double&`)は翻訳せず保持 - コードの構造とフォーマットを完全に維持

パックインデックス指定子

typedef-name ...[ expression ]

パックインデックス指定子は computed-type-specifier を表し、パック要素 P I の型を示します。 typedef-name type template parameter pack の宣言によって導入されなければなりません。

template <typename... Ts>
using last_type_t = Ts...[sizeof...(Ts) - 1];
static_assert(std::is_same_v<last_type_t<>, int>); // エラー: 範囲外
static_assert(std::is_same_v<last_type_t<int>, int>);
static_assert(std::is_same_v<last_type_t<bool, char>, char>);
static_assert(std::is_same_v<last_type_t<float, int, bool*>, bool*>);

パックインデックス指定子は次のように記述できます:

パックインデックス指定子は、関数またはコンストラクタのパラメータリストで使用して、テンプレート引数推論における 非推論コンテキスト を確立することができます。

template <typename...>
struct type_seq {};
template <typename... Ts>
auto f(Ts...[0] arg, type_seq<Ts...>)
{
    return arg;
}
// OK: "Hello" は暗黙的に 'std::string_view' に変換される
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
// エラー: "Ok" は 'int' に変換できない
std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});

注記

C++26より前では、 Ts... [ N ] は無名配列のサイズ N の関数パラメータパックを宣言する有効な構文でした。ここでパラメータ型はさらにポインタ型に調整されていました。C++26以降、 Ts... [ 1 ] はパックインデックス指定子として解釈され、以下の動作を#2に変更します。最初の動作を維持するには、関数パラメータパックに名前を付けるか、手動でポインタ型のパックに調整する必要があります。

template <typename... Ts>
void f(Ts... [1]);
template <typename... Ts>
void g(Ts... args[1]);
template <typename... Ts>
void h(Ts*...); // より明確だがより許容的: Ts...はcv voidまたは関数型を含む可能性がある
void foo() 
{
    f<char, bool>(nullptr, nullptr);
    // 動作 #1 (C++26以前):
    //  void 'f<char, bool>(char*, bool*)' (別名 'f<char, bool>(char[1], bool[1])') を呼び出す
    // 動作 #2 (C++26以降): 
    //  エラー: 'void f<char, bool>(bool)' が呼び出されたと推定されるが
    //  1つの引数ではなく2つの引数が指定されている
    g<char, bool>(nullptr, nullptr);
    // 'g<char, bool>(char*, bool*)' (別名 'g<char, bool>(char[1], bool[1])') を呼び出す
    h<char, bool>(nullptr, nullptr);
    // 'h<char, bool>(char*, bool*)' を呼び出す
}
機能テストマクロ 規格 機能
__cpp_pack_indexing 202311L (C++26) Pack indexing

#include <tuple>
template <std::size_t... Indices, typename Decomposable>
constexpr auto splice(Decomposable d)
{
    auto [...elems] = d;
    return std::make_tuple(elems...[Indices]...);
}
struct Point
{
    int x;
    int y;
    int z;
};
int main() 
{
    constexpr Point p { .x = 1, .y = 4, .z = 3 };
    static_assert(splice<2, 1, 0>(p) == std::make_tuple(3, 4, 1));
    static_assert(splice<1, 1, 0, 0>(p) == std::make_tuple(4, 4, 1, 1));
}