Namespaces
Variants

Aggregate initialization

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

집계체를 초기화자 목록으로 초기화합니다 . 이것은 list-initialization 의 한 형태입니다 (since C++11) .

목차

구문

T 객체 = { 인수1, 인수2, ... }; (1)
T 객체 { 인수1, 인수2, ... }; (2) (C++11부터)
T 객체 = { . 지정자1 = 인수1 , . 지정자2 { 인수2 } ... }; (3) (C++20부터)
T 객체 { . 지정자1 = 인수1 , . 지정자2 { 인수2 } ... }; (4) (C++20부터)
1,2) 일반 초기화 리스트를 사용한 집합체 초기화.
3,4) 지정자 초기화 를 사용한 집합체 초기화(집합체 클래스만 해당).

정의

집계체

집합체(aggregate) 는 다음 유형 중 하나입니다:

  • array types
  • class types that has
  • 사용자 선언 생성자 없음
(C++11 이전)
(C++11 이후)
(C++20 이전)
  • 사용자 선언 또는 상속된 생성자 없음
(C++20 이후)
  • private 또는 protected 직접 비정적 데이터 멤버 없음
(C++17까지)
(C++17부터)
  • 가상 멤버 함수 없음
(C++11부터)
(C++14까지)

요소

집합체의 요소들 은 다음과 같습니다:

  • 배열의 경우, 배열 요소는 증가하는 첨자 순서로, 또는
  • 클래스의 경우, 익명 bit-fields 가 아닌 비정적 데이터 멤버들을 선언 순서대로.
(until C++17)
  • 클래스의 경우, 선언 순서대로 직접 기본 클래스들, 그 다음으로 익명 bit-fields 가 아니고 anonymous union 의 멤버도 아닌 직접 비정적 데이터 멤버들을 선언 순서대로.
(since C++17)

관련 사항

중괄호로 둘러싸인 초기화 리스트의 각 initializer clause 는 초기화되는 aggregate의 요소 또는 그 하위 aggregate의 요소 중 하나에 속한다(appertain) 고 말합니다.

초기화 절의 시퀀스와, 아래에 설명된 대로 잠재적으로 수정될 수 있는 초기화되는 집합체의 요소 시퀀스로서 초기 형성된 집합체 요소들의 시퀀스를 고려합니다:

  • 각 초기화 절에 대해, 다음 조건 중 하나라도 만족되면 해당 집계 요소 elem 에 속합니다:
  • elem 는 aggregate가 아닙니다.
  • 초기화 절이 { 로 시작합니다.
  • 초기화 절이 표현식이며, 해당 표현식을 elem 의 타입으로 변환할 수 있는 암시적 변환 시퀀스 가 형성될 수 있습니다.
  • elem 은 자체적으로 aggregate 요소를 갖지 않는 aggregate입니다.
  • 그렇지 않으면, elem 는 집계체(aggregate)이며, 해당 하위 집계체는 집계체 요소 목록에서 자체 집계체 요소들의 시퀀스로 대체되고, 부속 관계 분석은 첫 번째 해당 요소와 동일한 초기화 절로 재개됩니다. 즉, 이러한 규칙들은 집계체의 하위 집계체들에 대해 재귀적으로 적용됩니다.

모든 초기화 절이 소진되면 분석이 완료됩니다. 집계체의 요소나 그 하위 집계체에 속하지 않는 초기화 절이 남아 있는 경우 프로그램은 올바르지 않은 형태입니다.

struct S1 { long a, b; };
struct S2 { S1 s, t; };
// "x"의 각 하위 집계체는 {로 시작하는 초기화 절에 귀속됩니다
S2 x[2] =
{
    // "x[0]"에 귀속
    {
        {1L, 2L}, // "x[0].s"에 귀속
        {3L, 4L}  // "x[0].t"에 귀속
    },
    // "x[1]"에 귀속
    {
        {5L, 6L}, // "x[1].s"에 귀속
        {7L, 8L}  // "x[1].t"에 귀속
    }
};
// "x"와 "y"는 동일한 값을 가짐 (아래 참조)
S2 y[2] = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L};
// "y"의 귀속 분석 과정:
// 1. 집계체 원소 시퀀스 (x[0], x[1])와
//    초기화 절 시퀀스 (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L)를 초기화합니다.
// 2. 각 시퀀스의 첫 번째 원소부터 시작하여,
//    1L이 x[0]에 귀속되는지 확인합니다:
//    · x[0]은 집계체입니다.
//    · 1L은 {로 시작하지 않습니다.
//    · 1L은 표현식이지만 S2로 암시적으로 변환될 수 없습니다.
//    · x[0]은 집계체 원소를 가집니다.
// 3. 1L이 x[0]에 귀속될 수 없으므로, x[0]은 x[0].s와 x[0].t로 대체되고,
//    집계체 원소 시퀀스는 (x[0].s, x[0].t, x[1])이 됩니다.
// 4. 귀속 확인을 재개하지만, 1L은 x[0].s에도 귀속될 수 없습니다.
// 5. 집계체 원소 시퀀스는 이제 (x[0].s.a, x[0].s.b, x[0].t, x[1])이 됩니다.
// 6. 귀속 확인을 다시 재개합니다:
//    1L은 x[0].s.a에 귀속되고, 2L은 x[0].s.b에 귀속됩니다.
// 7. 나머지 귀속 분석도 유사하게 진행됩니다.
char cv[4] = {'a', 's', 'd', 'f', 0}; // 오류: 초기화 절이 너무 많음

초기화 과정

요소 종류 결정

집합체 초기화의 효과는 다음과 같습니다:

1) 집합체의 명시적으로 초기화된 요소들 을 다음과 같이 결정합니다:
  • 초기화자 목록이 지정 초기화자 목록 인 경우(집계체는 클래스 타입만 가능), 각 지정자 내 식별자는 클래스의 직접 비정적 데이터 멤버를 지정해야 하며, 집계체의 명시적으로 초기화된 요소들은 해당 멤버들이거나 해당 멤버들을 포함하는 요소들입니다.
(C++20부터)
  • 그렇지 않으면, (C++20부터) 초기화자 리스트가 비어 있지 않은 경우, 집계체의 명시적으로 초기화된 요소들은 해당 초기화자 절이 부착된 요소들과 해당 초기화자 절이 부착된 하위 집계체를 가진 요소들입니다.
  • 그렇지 않으면, 초기화자 리스트는 비어 있어야 합니다 ( { } ), 그리고 명시적으로 초기화된 요소가 없습니다.
프로그램은 집계체가 공용체이고 두 개 이상의 명시적으로 초기화된 요소가 있는 경우 잘못된 형식입니다:
union u { int a; const char* b; };
u a = {1};                   // OK: 명시적으로 멤버 `a`를 초기화함
u b = {0, "asdf"};           // 오류: 두 멤버를 명시적으로 초기화함
u c = {"asdf"};              // 오류: int는 "asdf"로 초기화할 수 없음
// C++20 지정 초기화 목록
u d = {.b = "asdf"};         // OK: 초기화되지 않은 멤버를 명시적으로 초기화할 수 있음
u e = {.a = 1, .b = "asdf"}; // 오류: 두 멤버를 명시적으로 초기화함
2) 집계체의 각 요소를 요소 순서대로 초기화합니다. 즉, 주어진 요소와 관련된 모든 값 계산 및 부수 효과는 순서상 그 뒤에 오는 모든 요소의 계산 및 효과보다 sequenced before 관계에 있습니다 (since C++11) .

명시적으로 초기화된 요소

명시적으로 초기화된 각 요소에 대해:

  • 요소가 익명 공용체 멤버이고 초기화 리스트가 지정 초기화 리스트 인 경우, 해당 요소는 지정 초기화 리스트 { D } 로 초기화됩니다. 여기서 D 는 익명 공용체 멤버의 멤버를 지정하는 지정 초기화 절입니다. 이러한 지정 초기화 절은 오직 하나만 존재해야 합니다.
struct C
{
    union
    {
        int a;
        const char* p;
    };
    int x;
} c = {.a = 1, .x = 3}; // c.a를 1로, c.x를 3으로 초기화
  • 그렇지 않고 초기화 리스트가 지정 초기화 리스트인 경우, 요소는 해당 지정 초기화 절의 초기화자로 초기화됩니다.
  • 해당 초기화자가 구문 (1) 에 해당하고, 표현식을 변환하기 위해 축소 변환이 필요한 경우 프로그램은 잘못된 형식입니다.
(C++20부터)


  • 이니셜라이저 리스트는 중괄호로 둘러싸인 이니셜라이저 리스트입니다:
(until C++20)
  • 그렇지 않으면, 이니셜라이저 리스트는 비지정 중괄호로 둘러싸인 이니셜라이저 리스트입니다:
(since C++20)
  • 초기화 절이 집합체 요소에 속하는 경우, 해당 집합체 요소는 copy-initialized 됩니다.
  • 그렇지 않은 경우, 집합체 요소는 해당 집합체 요소의 하위 객체에 속하는 모든 초기화 절들로 구성된 brace-enclosed initializer list로부터 copy-initialized 됩니다.
struct A
{
    int x;
    struct B
    {
        int i;
        int j;
    } b;
} a = {1, {2, 3}}; // a.x를 1로, a.b.i를 2로, a.b.j를 3으로 초기화
struct base1 { int b1, b2 = 42; };
struct base2
{
    base2()
    {
        b3 = 42;
    }
    int b3;
};
struct derived : base1, base2
{
    int d;
};
derived d1{{1, 2}, {}, 4}; // d1.b1을 1로, d1.b2를 2로,
                           //             d1.b3을 42로, d1.d를 4로 초기화
derived d2{{}, {}, 4};     // d2.b1을 0으로, d2.b2를 42로,
                           //             d2.b3을 42로, d2.d를 4로 초기화

암시적으로 초기화된 요소들

비-공용체 집합체의 경우, 명시적으로 초기화되지 않은 각 요소는 다음과 같이 초기화됩니다:

(since C++11)
  • 그렇지 않고 요소가 참조가 아닌 경우, 요소는 빈 초기화자 목록으로부터 copy-initialized 됩니다.
  • 그렇지 않은 경우, 프로그램은 형식에 맞지 않습니다.
struct S
{
    int a;
    const char* b;
    int c;
    int d = b[a];
};
// ss.a를 1로 초기화하고,
// ss.b를 "asdf"로 초기화하고,
// ss.c를 int{} 형태의 표현식 값(즉, 0)으로 초기화하고,
// ss.d를 ss.b[ss.a] 값(즉, 's')으로 초기화합니다
S ss = {1, "asdf"};

집합체가 공용체이고 초기화 리스트가 비어 있는 경우, 다음이 적용됩니다.

  • 어떤 variant 멤버가 기본 멤버 초기화자를 가지고 있다면, 해당 멤버는 기본 멤버 초기화자로부터 초기화됩니다.
(since C++11)
  • 그렇지 않으면, 해당 union의 첫 번째 멤버(존재하는 경우)가 빈 초기화자 목록으로부터 복사 초기화됩니다.

크기를 알 수 없는 배열

알 수 없는 경계를 가진 배열을 중괄호로 둘러싼 초기화 목록으로 초기화할 때 배열의 요소 수는 명시적으로 초기화된 요소의 수입니다. 알 수 없는 경계를 가진 배열은 { } 로 초기화할 수 없습니다.

int x[] = {1, 3, 5}; // x는 3개의 요소를 가짐
struct Y { int i, j, k; };
Y y[] = {1, 2, 3, 4, 5, 6}; // y는 단 2개의 요소만 가짐:
                            // 1, 2, 3은 y[0]에 속하고,
                            // 4, 5, 6은 y[1]에 속함
int z[] = {} // 오류: 요소가 없는 배열을 선언할 수 없음

지정 초기화자(Designated initializers)

(3,4) 형식의 문법은 지정 초기화자로 알려져 있습니다: 각 지정자(designator) 는 T의 직접 비정적 데이터 멤버를 지정해야 하며, 표현식에서 사용된 모든 지정자(designator) 는 T의 데이터 멤버 순서와 동일한 순서로 나타나야 합니다.

struct A { int x; int y; int z; };
A a{.x = 1, .y = 2, .z = 3}; // ok
A b{.y = 2, .z = 3, .x = 1}; // error; designator order does not match declaration order

지정 초기화자로 명명된 각 직접 비정적 데이터 멤버는 지정자 뒤에 오는 해당 중괄호 또는 등호 초기화자로부터 초기화됩니다. 축소 변환은 금지됩니다.

지정 초기화자는 union 을 첫 번째 상태가 아닌 다른 상태로 초기화하는 데 사용할 수 있습니다. union에는 하나의 초기화자만 제공될 수 있습니다.

union u { int a; const char* b; };
u f = {.b = "asdf"};         // OK, active member of the union is b
u g = {.a = 1, .b = "asdf"}; // Error, only one initializer may be provided

비-union 집계체의 경우, 지정 초기화자가 제공되지 않은 요소들은 초기화자 절의 수가 멤버 수보다 적을 때와 동일하게 초기화됩니다 (제공된 경우 기본 멤버 초기화자, 그렇지 않으면 빈 목록 초기화):

struct A
{
    string str;
    int n = 42;
    int m = -1;
};
A{.m = 21} // Initializes str with {}, which calls the default constructor
           // then initializes n with = 42
           // then initializes m with = 21
struct A { int x; int y; int z; };
A a{.x = 1, .z = 2}; // ok, b.y initialized to 0
A b{.y = 2, .x = 1}; // error; designator order does not match declaration order
A c{.y = 2}; // ok, c.x and c.z are initialized to 0
constexpr A d{.z = 2}; // can be used with constexpr, as opposed to: constexpr A d;
static_assert(d.x == 0 && d.y == 0); // d.x and d.y are initialized to 0

지정 초기화자 절로 초기화되는 집계체가 익명 union 멤버를 가지고 있는 경우, 해당 지정 초기화자는 해당 익명 union의 멤버 중 하나를 지정해야 합니다.

참고: 순서가 바뀐 지정 초기화, 중첩 지정 초기화, 지정 초기화자와 일반 초기화자의 혼합, 그리고 배열의 지정 초기화는 모두 C 프로그래밍 언어 에서 지원되지만 C++에서는 허용되지 않습니다.

struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5};        // valid C, invalid C++ (array)
struct B b = {.a.x = 0};       // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2};      // valid C, invalid C++ (mixed)
(C++20부터)

문자 배열

일반 문자 타입의 배열( char , signed char , unsigned char ) , char8_t (C++20부터) , char16_t , char32_t (C++11부터) , 또는 wchar_t )은 각각 일반 문자열 리터럴 , UTF-8 문자열 리터럴 (C++20부터) , UTF-16 문자열 리터럴, UTF-32 문자열 리터럴 (C++11부터) 또는 와이드 문자열 리터럴로부터 초기화될 수 있으며, 선택적으로 중괄호로 둘러싸일 수 있습니다 . 추가적으로, char 또는 unsigned char 배열은 UTF-8 문자열 리터럴로 초기화될 수 있으며, 선택적으로 중괄호로 둘러싸일 수 있습니다 (C++20부터) . 문자열 리터럴의 연속된 문자들(암시적 널 종결 문자 포함)은 배열의 요소들을 초기화합니다 , 소스와 대상 값에 필요한 경우 정수 변환 을 수행합니다 (C++20부터) . 배열의 크기가 지정되고 문자열 리터럴의 문자 수보다 큰 경우, 나머지 문자들은 0으로 초기화됩니다.

char a[] = "abc";
// char a[4] = {'a', 'b', 'c', '\0'};와 동일
//  unsigned char b[3] = "abc"; // 오류: 초기화 문자열이 너무 김
unsigned char b[5]{"abc"};
// unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};와 동일
wchar_t c[] = {L"кошка"}; // 선택적 중괄호
// wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};와 동일

참고 사항

집계 클래스 또는 배열은 비집계 public 기반 클래스 (C++17부터) , 멤버, 또는 요소를 포함할 수 있으며, 이들은 위에서 설명된 방식으로 초기화됩니다(예: 해당 초기화 절에서의 복사 초기화).

C++11 이전에는 집합체 초기화에서 축소 변환이 허용되었지만, 이제는 더 이상 허용되지 않습니다.

C++11 이전까지는, 집합체 초기화(aggregate initialization)는 변수 정의에서만 사용할 수 있었으며, 문법적 제약으로 인해 생성자 초기화 리스트 new-expression , 또는 임시 객체 생성에서는 사용할 수 없었습니다.

C에서는 문자열 리터럴의 크기보다 하나 작은 크기의 문자 배열을 문자열 리터럴로 초기화할 수 있습니다; 결과 배열은 null로 종료되지 않습니다. 이것은 C++에서는 허용되지 않습니다.

기능 테스트 매크로 표준 기능
__cpp_aggregate_bases 201603L (C++17) 기본 클래스를 갖는 집계 클래스
__cpp_aggregate_nsdmi 201304L (C++14) 기본 멤버 초기화자를 갖는 집계 클래스
__cpp_aggregate_paren_init 201902L (C++20) 직접 초기화 형태의 집계 초기화
__cpp_char8_t 202207L (C++23)
(DR20)
char8_t 호환성 및 이식성 수정 ( ( unsigned char 배열의 UTF-8 문자열 리터럴 에서의 초기화 허용)
__cpp_designated_initializers 201707L (C++20) 지정 초기화자

예제

#include <array>
#include <cstdio>
#include <string>
struct S
{
    int x;
    struct Foo
    {
        int i;
        int j;
        int a[3];
    } b;
};
int main()
{
    S s1 = {1, {2, 3, {4, 5, 6}}};
    S s2 = {1, 2, 3, 4, 5, 6}; // 동일하지만 중괄호 생략 사용
    S s3{1, {2, 3, {4, 5, 6}}}; // 동일, 직접 목록 초기화 구문 사용
    S s4{1, 2, 3, 4, 5, 6}; // CWG 1270까지 오류:
                            // 중괄호 생략은 등호 기호와 함께만 허용됨
    int ar[] = {1, 2, 3}; // ar은 int[3]
//  char cr[3] = {'a', 'b', 'c', 'd'}; // 초기화 절이 너무 많음
    char cr[3] = {'a'}; // 배열이 {'a', '\0', '\0'}으로 초기화됨
    int ar2d1[2][2] = {{1, 2}, {3, 4}}; // 완전한 중괄호 2D 배열: {1, 2}
                                        //                        {3, 4}
    int ar2d2[2][2] = {1, 2, 3, 4}; // 중괄호 생략: {1, 2}
                                    //                {3, 4}
    int ar2d3[2][2] = {{1}, {2}};   // 첫 번째 열만: {1, 0}
                                    //                    {2, 0}
    std::array<int, 3> std_ar2{{1, 2, 3}};  // std::array는 집계체임
    std::array<int, 3> std_ar1 = {1, 2, 3}; // 중괄호 생략 가능
//  int ai[] = {1, 2.0}; // double에서 int로의 축소 변환:
                         // C++11에서는 오류, C++03에서는 허용
    std::string ars[] = {std::string("one"), // 복사 초기화
                         "two",              // 변환 후 복사 초기화
                         {'t', 'h', 'r', 'e', 'e'}}; // 목록 초기화
    union U
    {
        int a;
        const char* b;
    };
    U u1 = {1};         // OK, 공용체의 첫 번째 멤버
//  U u2 = {0, "asdf"}; // 오류: 공용체에 대한 초기화 값이 너무 많음
//  U u3 = {"asdf"};    // 오류: int로의 잘못된 변환
    [](...) { std::puts("Garbage collecting unused variables... Done."); }
    (
        s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1
    );
}
// 집계체
struct base1 { int b1, b2 = 42; };
// 비집계체
struct base2
{
    base2() : b3(42) {}
    int b3;
};
// C++17에서 집계체
struct derived : base1, base2 { int d; };
derived d1{{1, 2}, {}, 4}; // d1.b1 = 1, d1.b2 = 2,  d1.b3 = 42, d1.d = 4
derived d2{{}, {}, 4};     // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4

출력:

Garbage collecting unused variables... Done.

결함 보고서

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

DR 적용 대상 게시된 동작 올바른 동작
CWG 413 C++98 익명 비트 필드가 집합체 초기화에서 초기화됨 무시됨
CWG 737 C++98 문자 배열이 배열 크기보다 짧은 문자열 리터럴로 초기화될 때
trailing ' \0 ' 이후의 문자
요소들이 초기화되지 않음
해당 요소들이
0으로 초기화됨
CWG 1270 C++11 brace elision이 복사 목록 초기화에서만 사용 가능했음 다른 곳에서도 허용됨
CWG 1518 C++11 명시적 기본 생성자를 선언하거나 상속된 생성자를 가진
클래스가 집합체가 될 수 있음
집합체가
아님
CWG 1622 C++98 union을 { } 로 초기화할 수 없음 허용됨
CWG 2149
( P3106R1 )
C++98 brace elision이 배열 크기 추론 중에 적용 가능한지
불분명했음
적용 가능함
CWG 2272 C++98 명시적으로 초기화되지 않은 비정적 참조 멤버가
빈 초기화자 목록에서 복사 초기화됨
이 경우 프로그램이
ill-formed
CWG 2610 C++17 집합체 타입이 private 또는 protected 간접 기본 클래스를
가질 수 없음
허용됨
CWG 2619 C++20 지정 초기화자로부터의 초기화 종류가 불분명했음 초기화자의
종류에 따라 다름
P2513R4 C++20 UTF-8 문자열 리터럴이 char
또는 unsigned char 배열을 초기화할 수 없어
C 또는 C++17과 호환되지 않음
이러한 초기화가
유효함

참고 항목

C 문서 참조: 구조체 및 공용체 초기화