Aggregate initialization
집계체를 초기화자 목록으로 초기화합니다 . 이것은 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부터) | |||||||
정의
집계체
집합체(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까지) |
요소
집합체의 요소들 은 다음과 같습니다:
- 배열의 경우, 배열 요소는 증가하는 첨자 순서로, 또는
|
(until C++17) |
|
(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}; // 오류: 초기화 절이 너무 많음
초기화 과정
요소 종류 결정
집합체 초기화의 효과는 다음과 같습니다:
|
(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"}; // 오류: 두 멤버를 명시적으로 초기화함
명시적으로 초기화된 요소
명시적으로 초기화된 각 요소에 대해:
struct C { union { int a; const char* p; }; int x; } c = {.a = 1, .x = 3}; // c.a를 1로, c.x를 3으로 초기화
|
(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"};
집합체가 공용체이고 초기화 리스트가 비어 있는 경우, 다음이 적용됩니다.
|
(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 문서
참조:
구조체 및 공용체 초기화
|