Pack indexing (since C++26)
지정된 인덱스에서 pack 의 요소에 접근합니다.
목차 |
구문
id-expression
...[
expression
]
|
(1) | ||||||||
typedef-name
...[
expression
]
|
(2) | ||||||||
| 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*>);
팩 인덱싱 지정자는 다음과 같이 나타날 수 있습니다:
- 단순 타입 지정자(simple type specifier) ,
- 기본 클래스 지정자(base class specifier) ,
- 중첩 이름 지정자(nested name specifier) , 또는
- 명시적 소멸자 호출의 타입(type of an explicit destructor call) .
팩 인덱싱 지정자는 함수나 생성자 매개변수 목록에서 사용되어 비추론 컨텍스트 를 템플릿 인수 추론 과정에서 설정하는 데 사용될 수 있습니다.
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)); }