名前空間
変種
操作

パックインデックス化 (C++26以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 
 
 

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

目次

[編集] 構文

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

[編集] 説明

パックインデックス化は、展開されていないパックに省略符号と添字内のインデックスが続くパック展開です。パックインデックス化には、パックインデックス化式とパックインデックス化指定子の2種類があります。

PP0, P1, ..., Pn-1 を含む空でないパックとし、I を有効なインデックスとすると、展開 P...[I] のインスタンス化は P のパック要素 PI を生成します。

非定数式インデックス 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]; // error: 'm' is not a constant expression
    auto d = args...[runtime_idx()]; // error: 'runtime_idx()' is not a constant expression
}

テンプレートテンプレートパラメータのパックをインデックス化することはできません。

template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // error: 'Temps' is a pack of template template parameters
 
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // error: 'Temps<>' doesn't denote pack name 
                         // although it is a simple-template-id

[編集] パックインデックス化式

id-expression ...[ expression ]

パックインデックス化式は、id-expression、つまりパック要素 PI の式を示します。id-expression は以下の宣言によって導入されなければなりません。

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
    // 'args' introduced in function parameter pack declaration
    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); // error:  out of bounds
static_assert(element_at<0>() == 1); // error: out of bounds, empty pack
 
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
    auto [...elems] = tup;
    // 'elems' introduced in structured binding pack declaration
    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' introduced in non-type template parameter pack declaration
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' introduced in lambda init-capture pack
        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]; // error
// use 'Vals...[I]' instead
 
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t triple_at = (Vals * 3)...[I]; // error
// use 'Vals...[I] * 3' instead
 
template <std::size_t I, typename... Args>
constexpr decltype(auto) get(Args&&... args) noexcept
{
    return std::forward<Args>(args)...[I]; // error
    // use 'std::forward<Args...[I]>(args...[I])' instead
}

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

void f() 
{
    [](auto... args)
    {
        using T0 = decltype(args...[0]);   // 'T0' is 'double'
        using T1 = decltype((args...[0])); // 'T1' is 'double&'
    }(3.14);
}

[編集] パックインデックス化指定子

typedef-name ...[ expression ]

パックインデックス化指定子は、computed-type-specifier、つまりパック要素 PI の型を示します。typedef-name型テンプレートパラメータパックの宣言によって導入されなければなりません。

template <typename... Ts>
using last_type_t = Ts...[sizeof...(Ts) - 1];
 
static_assert(std::is_same_v<last_type_t<>, int>); // error: out of bounds
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" is implicitly converted to 'std::string_view'
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
 
// Error: "Ok" is not convertible to '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*...); // clearer but more permissive: Ts... can contain cv void or function types
 
void foo() 
{
    f<char, bool>(nullptr, nullptr);
    // behavior #1 (before C++26):
    //  calls void 'f<char, bool>(char*, bool*)' (aka 'f<char, bool>(char[1], bool[1])')
    // behavior #2 (since C++26): 
    //  error: supposedly called 'void f<char, bool>(bool)'
    //  but provided with 2 arguments instead of 1
 
    g<char, bool>(nullptr, nullptr);
    // calls 'g<char, bool>(char*, bool*)' (aka 'g<char, bool>(char[1], bool[1])')
 
    h<char, bool>(nullptr, nullptr);
    // calls 'h<char, bool>(char*, bool*)'
}
機能テストマクロ 規格 機能
__cpp_pack_indexing 202311L (C++26) パックインデックス

[編集]

#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));
}
English 日本語 中文(简体) 中文(繁體)