Namespaces
Variants

Structured binding declaration (since C++17)

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

지정된 이름들을 초기화자의 하위 객체 또는 요소에 바인딩합니다.

참조와 마찬가지로, 구조적 바인딩은 기존 객체에 대한 별칭입니다. 참조와 달리, 구조적 바인딩은 반드시 참조 타입일 필요는 없습니다.

attr  (선택 사항) decl-specifier-seq ref-qualifier  (선택 사항) [ sb-identifier-list ] initializer  ;
attr - 임의 개수의 속성 시퀀스
decl-specifier-seq - 다음 지정자들의 시퀀스 ( 단순 선언 규칙 따름):
(C++26부터)
ref-qualifier - & 또는 && 중 하나
sb-identifier-list - 이 선언으로 도입되는 쉼표로 구분된 식별자 목록 , 각 식별자 뒤에는 속성 지정자 시퀀스 가 올 수 있음 (C++26부터)
initializer - 초기화자 (아래 참조)


initializer 는 다음 중 하나일 수 있습니다:

= 표현식 (1)
{ 표현식 } (2)
( 표현식 ) (3)
expression - 임의의 표현식 (괄호로 묶이지 않은 comma expressions 제외)


구조화된 바인딩 선언은 sb-identifier-list 내의 모든 식별자를 주변 범위에서 이름으로 도입하고 이를 expression 이 나타내는 객체의 하위 객체나 요소에 바인딩합니다. 이렇게 도입된 바인딩을 structured bindings (구조화된 바인딩)이라고 합니다.

sb-identifier-list 내의 식별자 중 하나는 생략표(...)로 시작할 수 있습니다. 이러한 식별자는 structured binding pack 을 도입합니다.

해당 식별자는 templated entity 를 선언해야 합니다.

(since C++26)

구조적 바인딩은 sb-identifier-list  에서 생략표시 앞에 오지 않는 식별자이거나, 동일한 식별자 목록에서 도입된 구조적 바인딩 팩의 요소 (C++26부터) 입니다.

목차

바인딩 프로세스

구조적 바인딩 선언은 먼저 초기화식의 값을 보유하기 위해 고유한 이름의 변수(여기서는 e 로 표시됨)를 도입합니다. 다음과 같습니다:

  • 만약 expression 이 배열 타입 cv1 A 를 가지고 있고 ref-qualifier 가 존재하지 않는 경우, e attr  (optional) specifiers A e; 로 정의합니다. 여기서 specifiers decl-specifier-seq 내의 지정자들 중 auto 를 제외한 시퀀스입니다.
그런 다음 e 의 각 요소는 expression 의 해당 요소로부터 initializer 의 형태에 따라 다음과 같이 초기화됩니다:
  • 그렇지 않으면, e attr  (optional) decl-specifier-seq ref-qualifier  (optional) e initializer  ; 로 정의합니다.

우리는 식별자 표현식 e 의 타입을 나타내기 위해 E 를 사용합니다 (즉, E std:: remove_reference_t < decltype ( ( e ) ) > 에 해당합니다).

구조적 바인딩 크기 는 구조적 바인딩 선언에 의해 도입되어야 하는 구조적 바인딩의 개수입니다.

sb-identifier-list 내 식별자의 개수는 E 의 구조적 바인딩 크기와 동일해야 합니다.

(until C++26)

sb-identifier-list 내 식별자의 개수를 N 으로, E 의 구조적 바인딩 크기를 S 로 지정합니다:

  • 구조적 바인딩 팩이 없는 경우, N S 와 같아야 합니다.
  • 그렇지 않으면, 비-팩 요소의 개수(즉, N - 1 )는 S 보다 작거나 같아야 하며, 구조적 바인딩 팩의 요소 개수는 S - N + 1 입니다(이는 0일 수 있습니다).
(since C++26)
struct C { int x, y, z; };
template<class T>
void now_i_know_my() 
{
    auto [a, b, c] = C(); // 정상: a, b, c는 각각 x, y, z를 참조
    auto [d, ...e] = C(); // 정상: d는 x를 참조; ...e는 y와 z를 참조
    auto [...f, g] = C(); // 정상: ...f는 x와 y를 참조; g는 z를 참조
    auto [h, i, j, ...k] = C();    // 정상: 패킷 k는 비어 있음
    auto [l, m, n, o, ...p] = C(); // 오류: 구조화된 바인딩 크기가 너무 작음
}

구조적 바인딩 선언은 다음 세 가지 방법 중 하나로 바인딩을 수행하며, 이는 E 에 따라 달라집니다:

  • 경우 1: E 가 배열 타입인 경우, 이름들은 배열 요소들에 바인딩됩니다.
  • 경우 2: E 가 비-공용체 클래스 타입이고 std:: tuple_size < E > value 라는 멤버를 가진 완전한 타입인 경우(해당 멤버의 타입이나 접근성과 무관하게), "튜플-유사" 바인딩 프로토콜이 사용됩니다.
  • 경우 3: E 가 비-공용체 클래스 타입이지만 std:: tuple_size < E > 가 완전한 타입이 아닌 경우, 이름들은 E 의 접근 가능한 데이터 멤버들에 바인딩됩니다.

세 가지 경우 각각에 대해 아래에서 더 자세히 설명합니다.

각 구조화된 바인딩에는 아래 설명에서 정의된 참조 타입 이 있습니다. 이 타입은 괄호 없는 구조화된 바인딩에 decltype 을 적용했을 때 반환되는 타입입니다.

사례 1: 배열 바인딩

sb-identifier-list 내의 각 구조적 바인딩은 배열의 해당 요소를 참조하는 lvalue의 이름이 됩니다. 구조적 바인딩의 크기는 E 의 배열 요소 개수와 동일합니다.

각 구조적 바인딩의 참조된 타입 은 배열 요소 타입입니다. 배열 타입 E 가 cv-qualified인 경우, 그 요소 타입도 cv-qualified라는 점에 유의하십시오.

int a[2] = {1, 2};
auto [x, y] = a;    // e[2]를 생성하고 a를 e로 복사한 다음,
                    // x는 e[0]을 참조하고 y는 e[1]을 참조함
auto& [xr, yr] = a; // xr은 a[0]을 참조하고 yr은 a[1]을 참조함

Case 2: 튜플 연산을 구현하는 타입 바인딩

표현식 std:: tuple_size < E > :: value 는 올바른 형태의 정수 상수 표현식 이어야 하며, E 의 구조적 바인딩 크기는 std:: tuple_size < E > :: value 와 동일해야 합니다.

각 구조적 바인딩에 대해 "참조 std:: tuple_element < I, E > :: type " 타입의 변수가 도입됩니다: 해당 초기화자가 lvalue인 경우 lvalue 참조, 그렇지 않으면 rvalue 참조입니다. I 번째 변수의 초기화자는 다음과 같습니다.

  • e. get < I > ( ) , E 의 범위에서 식별자 get 을 클래스 멤버 접근 조회로 검색할 때 첫 번째 템플릿 매개변수가 상수 매개변수인 함수 템플릿 선언을 하나 이상 발견하는 경우
  • 그렇지 않으면, get < I > ( e ) , 여기서 get 은 비 ADL 조회를 무시하고 인수 종속 조회 에 의해서만 검색됨

이러한 초기화 표현식에서, e 는 엔티티 e 의 타입이 lvalue 참조인 경우 lvalue입니다(이는 ref-qualifier & 이거나 && 이면서 초기화 표현식이 lvalue인 경우에만 발생합니다). 그렇지 않은 경우 xvalue입니다(이는 효과적으로 일종의 완벽한 전달을 수행합니다). I std::size_t prvalue이며, < I > 는 항상 템플릿 매개변수 목록으로 해석됩니다.

변수는 storage duration e 와 동일합니다.

구조적 바인딩은 해당 변수에 바인딩된 객체를 참조하는 lvalue의 이름이 됩니다.

참조된 타입 I 번째 구조적 바인딩에 대해 std:: tuple_element < I, E > :: type 입니다.

float x{};
char  y{};
int   z{};
std::tuple<float&, char&&, int> tpl(x, std::move(y), z);
const auto& [a, b, c] = tpl;
// Tpl = const std::tuple<float&, char&&, int> 사용
// a는 x를 참조하는 구조화된 바인딩을 나타냄 (get<0>(tpl)로부터 초기화됨)
// decltype(a)는 std::tuple_element<0, Tpl>::type, 즉 float&
// b는 y를 참조하는 구조화된 바인딩을 나타냄 (get<1>(tpl)로부터 초기화됨)
// decltype(b)는 std::tuple_element<1, Tpl>::type, 즉 char&&
// c는 tpl의 세 번째 구성 요소인 get<2>(tpl)를 참조하는 구조화된 바인딩을 나타냄
// decltype(c)는 std::tuple_element<2, Tpl>::type, 즉 const int

Case 3: 데이터 멤버에 바인딩

E 의 모든 비정적 데이터 멤버는 E 의 직접 멤버이거나 E 의 동일한 기본 클래스에 속해야 하며, e. name 로 명명될 때 구조적 바인딩 컨텍스트에서 올바르게 형성되어야 합니다. E 는 익명 공용체 멤버를 가질 수 없습니다. E 의 구조적 바인딩 크기는 비정적 데이터 멤버의 수와 동일합니다.

sb-identifier-list 내의 각 구조적 바인딩은 선언 순서대로 e 의 다음 멤버를 참조하는 lvalue의 이름이 됩니다(비트 필드는 지원됨). 이 lvalue의 타입은 e. mI 의 타입과 동일하며, 여기서 mI I 번째 멤버를 나타냅니다.

참조된 타입 I 번째 구조적 바인딩의 타입이 참조 타입이 아닌 경우 e. mI 의 타입이며, 그렇지 않은 경우 mI 의 선언된 타입입니다.

#include <iostream>
struct S
{
    mutable int x1 : 2;
    volatile double y1;
};
S f() { return S{1, 2.3}; }
int main()
{
    const auto [x, y] = f(); // x는 2비트 비트 필드를 식별하는 int lvalue
                             // y는 const volatile double lvalue
    std::cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // 오류: y는 const 한정됨
    std::cout << x << ' ' << y << '\n';  // -2 2.3
}

초기화 순서

sb-identifier-list 에서 I 번째 구조적 바인딩에 명명된 객체 또는 참조를 valI 라고 하자:

  • e 의 초기화는 모든 valI 의 초기화보다 먼저 순서가 정해집니다 .
  • valI 의 초기화는 I J 보다 작은 모든 valJ 의 초기화보다 먼저 순서가 정해집니다.

참고 사항

구조화된 바인딩은 제약 조건 을 가질 수 없습니다:

template<class T>
concept C = true;
C auto [x, y] = std::pair{1, 2}; // error: constrained
(C++20부터)

멤버 get 에 대한 조회는 일반적으로 접근성을 무시하며, 상수 템플릿 매개변수의 정확한 타입도 무시합니다. private template < char * > void get ( ) ; 멤버가 존재하면, 해당 멤버가 잘못된 형태임에도 불구하고 멤버 해석이 사용됩니다.

선언의 앞부분은 도입된 구조적 바인딩이 아닌 숨겨진 변수 e 에 적용됩니다:

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x와 y는 int& 타입입니다
auto [z, w] = std::tie(a, b);        // z와 w 역시 int& 타입입니다
assert(&z == &a);                    // 통과합니다

튜플-유사 해석은 다음 조건에서 항상 사용됩니다: std:: tuple_size < E > 가 멤버 value 를 갖는 완전한 타입인 경우, 심지어 그것이 프로그램을 부적합하게 만들더라도:

struct A { int x; };
namespace std
{
    template<>
    struct tuple_size<::A> { void value(); };
}
auto [x] = A{}; // 오류; "데이터 멤버" 해석은 고려되지 않습니다.

임시 객체에 대한 참조 바인딩의 일반적인 규칙들(수명 연장 포함)은 ref-qualifier 가 존재하고 해당 expression 가 prvalue인 경우 적용됩니다. 이러한 경우 숨겨진 변수 e 는 prvalue 표현식에서 materialized 된 임시 변수에 바인딩되는 참조이며, 그 수명을 연장합니다. 일반적으로, e 가 non-const lvalue reference인 경우 바인딩은 실패합니다:

int a = 1;
const auto& [x] = std::make_tuple(a); // 정상, 댕글링 참조가 아님
auto&       [y] = std::make_tuple(a); // 오류, rvalue std::tuple에 auto&를 바인딩할 수 없음
auto&&      [z] = std::make_tuple(a); // 역시 정상

decltype ( x ) , 여기서 x 가 구조화된 바인딩을 나타내는 경우, 해당 구조화된 바인딩의 참조 타입 을 명시합니다. 튜플-유사(tuple-like) 경우에는, 이는 std::tuple_element 가 반환하는 타입으로, 이 경우 숨겨진 참조가 항상 도입되더라도 참조 타입이 아닐 수 있습니다. 이는 바인딩 자체의 참조성이 단순한 구현 세부사항인, std::tuple_element 가 반환하는 타입을 가진 비정적 데이터 멤버들로 구성된 구조체에 바인딩하는 동작을 효과적으로 모방합니다.

std::tuple<int, int&> f();
auto [x, y] = f();       // decltype(x)는 int입니다
                         // decltype(y)는 int&입니다
const auto [z, w] = f(); // decltype(z)는 const int입니다
                         // decltype(w)는 int&입니다

구조화된 바인딩은 람다 표현식 으로 캡처할 수 없습니다:

#include <cassert>
int main()
{
    struct S { int p{6}, q{7}; };
    const auto& [b, d] = S{};
    auto l = [b, d] { return b * d; }; // valid since C++20
    assert(l() == 42);
}
(C++20 이전)


구조화된 바인딩의 크기는 sb-identifier-list 가 빈 구조화된 바인딩 팩만 도입할 수 있는 식별자를 정확히 하나 포함하는 한 0 이 허용됩니다.

auto return_empty() -> std::tuple<>;
template <class>
void test_empty()
{
    auto [] = return_empty(); // error
    auto [...args] = return_empty(); // OK, args is an empty pack
    auto [one, ...rest] = return_empty(); // error, structured binding size is too small
}
(C++26부터)
기능 테스트 매크로 표준 기능
__cpp_structured_bindings 201606L (C++17) 구조화된 바인딩
202403L (C++26) 속성을 갖는 구조화된 바인딩
202406L (C++26) 조건문으로서의 구조화된 바인딩 선언
202411L (C++26) 팩을 도입할 수 있는 구조화된 바인딩

키워드

auto

예제

#include <iomanip>
#include <iostream>
#include <set>
#include <string>
int main()
{
    std::set<std::string> myset{"hello"};
    for (int i{2}; i; --i)
    {
        if (auto [iter, success] = myset.insert("Hello"); success) 
            std::cout << "Insert is successful. The value is "
                      << std::quoted(*iter) << ".\n";
        else
            std::cout << "The value " << std::quoted(*iter)
                      << " already exists in the set.\n";
    {
    struct BitFields
    {
        // C++20: 비트 필드에 대한 기본 멤버 초기화
        int b : 4 {1}, d : 4 {2}, p : 4 {3}, q : 4 {4};
    };
    {
        const auto [b, d, p, q] = BitFields{};
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
    {
        const auto [b, d, p, q] = []{ return BitFields{4, 3, 2, 1}; }();
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
    {
        BitFields s;
        auto& [b, d, p, q] = s;
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
        b = 4, d = 3, p = 2, q = 1;
        std::cout << s.b << ' ' << s.d << ' ' << s.p << ' ' << s.q << '\n';
    }
}

출력:

Insert is successful. The value is "Hello".
The value "Hello" already exists in the set.
1 2 3 4
4 3 2 1
1 2 3 4
4 3 2 1

결함 보고서

다음 동작 변경 결함 보고서는 이전에 게시된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 2285 C++17 expression identifier-list 의 이름들을 참조할 수 있었음 이 경우 선언이
ill-formed임
CWG 2312 C++17 case 3에서 mutable 의 의미가 소실됨 그 의미가 여전히 유지됨
CWG 2313 C++17 case 2에서 구조화 바인딩 변수들을 재선언할 수 있었음 재선언할 수 없음
CWG 2339 C++17 case 2에서 I 의 정의가 누락됨 정의를 추가함
CWG 2341
( P1091R3 )
C++17 구조화 바인딩을
정적 저장 기간으로 선언할 수 없었음
허용됨
CWG 2386 C++17 "tuple-like" 바인딩 프로토콜이
std:: tuple_size < E > 가 완전한 타입일 때마다 사용됨
std:: tuple_size < E >
가 멤버 value 를 가질 때만 사용됨
CWG 2506 C++17 expression 이 cv-qualified 배열 타입인 경우,
cv-qualification이 E 로 이월됨
해당 cv-qualification을 버림
CWG 2635 C++20 구조화 바인딩에 제약을 줄 수 있었음 금지됨
CWG 2867 C++17 초기화 순서가 불명확했음 명확하게 함
P0961R1 C++17 case 2에서 어떤 종류의 get 이라도
조회되면 멤버 get 이 사용됨
상수 매개변수를 가진 함수 템플릿을
조회할 때만 사용됨
P0969R0 C++17 case 3에서 멤버들이 public이어야 했음 선언 문맥에서 접근 가능하기만 하면 됨

참고문헌

  • C++23 표준 (ISO/IEC 14882:2024):
  • 9.6 구조적 바인딩 선언 [dcl.struct.bind] (p: 228-229)
  • C++20 표준(ISO/IEC 14882:2020):
  • 9.6 구조화된 바인딩 선언 [dcl.struct.bind] (p: 219-220)
  • C++17 표준 (ISO/IEC 14882:2017):
  • 11.5 구조화된 바인딩 선언 [dcl.struct.bind] (p: 219-220)

참고 항목

(C++11)
lvalue 참조의 tuple 을 생성하거나 tuple을 개별 객체로 언패킹합니다
(함수 템플릿)