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) Pack indexing specifier
typedef-name - 팩을 지칭하는 식별자 또는 단순-템플릿-식별자
id-expression - 팩을 지칭하는 식별-표현식
expression - 팩 인덱싱에서 특정 팩 P 에 대해 범위 [ 0 , sizeof... ( P ) ) 내에 있는 인덱스로 지정된 std:: size_t 타입의 변환된 상수 표현식 I

설명

팩 인덱싱은 팩 확장 으로, 확장되지 않은 팩 뒤에 생략 부호와 아래첨자 내 인덱스가 옵니다. 팩 인덱싱에는 두 가지 종류가 있습니다: 팩 인덱싱 표현식과 팩 인덱싱 지정자.

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 를 식별자 표현식에 적용하는 것과 동일합니다.

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

팩 인덱싱 지정자

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;
}
// 정상: "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) 팩 인덱싱

예제

#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));
}