Default-initialization
이는 객체가 초기화자 없이 생성될 때 수행되는 초기화입니다.
목차 |
구문
T 객체
;
|
(1) | ||||||||
new
T
|
(2) | ||||||||
설명
기본 초기화는 세 가지 상황에서 수행됩니다:
디폴트 초기화의 효과는 다음과 같습니다:
-
만약
T가 (cv 한정자가 있을 수 있는) non-POD (until C++11) 클래스 타입인 경우, 생성자들을 고려하여 빈 인수 목록에 대해 오버로드 해결 을 수행합니다. 선택된 생성자( 기본 생성자 중 하나)는 새 객체의 초기값을 제공하기 위해 호출됩니다; -
만약
T가 배열 타입인 경우, 배열의 모든 요소가 기본 초기화됩니다; - 그 외의 경우에는 초기화가 수행되지 않습니다( 참고 참조).
|
초기화자가 없을 때 자동 저장 기간을 가진 (가능하다면 cv-qualified) 비-POD 클래스 타입(또는 이들의 배열)만이 기본 초기화되는 것으로 간주되었습니다. 동적 저장 기간을 가진 스칼라와 POD 타입은 초기화되지 않는 것으로 간주되었습니다 (C++11부터 이 상황은 기본 초기화의 한 형태로 재분류되었습니다). |
(until C++11) |
|
(C++11 이전) |
|
(C++11 이후) |
T
의 각
잠재적으로 생성되는
베이스 클래스는 const-default-constructible입니다.
미정의 값 및 오류 값
|
자동 또는 동적 저장 기간을 가진 객체에 대한 저장 공간이 확보될 때, 해당 객체는 indeterminate value 를 가집니다. 객체에 대해 초기화가 수행되지 않으면, 해당 객체는 값이 대체될 때까지 indeterminate value를 유지합니다. |
(C++26 이전) |
|
자동 또는 동적 저장 기간을 가진 객체에 대한 저장 공간이 확보될 때, 객체의 저장 공간을 구성하는 바이트들은 다음 초기값을 가집니다:
객체(including subobjects )에 대해 초기화가 수행되지 않으면, 이러한 바이트는 값이 대체될 때까지 초기값을 유지합니다.
|
(C++26 이후) |
평가가 불확정 값을 생성하는 경우, 그 동작은 undefined 입니다.
|
평가가 잘못된 값을 생성하는 경우, 그 동작은 잘못된 것입니다. |
(since C++26) |
특수 케이스
다음 유형들은 초기화되지 않은 상태를 허용하는(uninitialized-friendly) 유형입니다:
| (C++17부터) |
- unsigned char
- char , 기본 타입이 unsigned char 인 경우
부정확하거나 오류가 있는 (C++26부터) 값 value 이 주어졌을 때, value 의 초기화되지 않은 결과 값 은 다음과 같습니다:
- 부정 값(indeterminate value), 만약 value 또한 부정 값인 경우.
|
(C++26부터) |
평가가 eval 가 초기화 친화적 타입의 불확정 또는 오류 (C++26부터) 값 value 을 생성하는 경우, 다음 상황에서의 동작은 명확히 정의됩니다:
- eval 는 다음 표현식과 피연산자 중 하나의 평가입니다:
-
- 조건부 표현식 의 두 번째 또는 세 번째 피연산자.
- 쉼표 표현식 의 오른쪽 피연산자.
-
초기화되지 않은-친화적(uninitialized-friendly) 타입으로의
정수 변환
,
명시적 캐스트
또는
static_cast의 피연산자. - 버려지는-값 표현식 .
- 이 경우 연산의 결과는 value 의 초기화되지 않은 결과 값입니다.
- eval 는 왼쪽 피연산자가 초기화-친화적(uninitialized-friendly) 타입의 lvalue인 단순 할당 연산자 의 오른쪽 피연산자 평가입니다.
- 이 경우, 왼쪽 피연산자가 참조하는 객체의 값은 value 의 초기화되지 않은 결과 값으로 대체됩니다.
- eval 는 초기화되지 않은 상태를 허용하는 타입의 객체를 초기화할 때 초기화 표현식의 평가입니다.
| (C++17부터) |
- 이 경우, 해당 객체는 value 의 초기화되지 않은 결과 값으로 초기화됩니다.
초기화에 친화적인 타입의 미정의 값을 변환하면 미정의 값이 생성됩니다.
|
초기화 친화적 타입의 오류 값을 변환하면 오류 값이 생성되며, 변환 결과는 변환된 피연산자의 값입니다. |
(since C++26) |
// Case 1: 동적 저장 기간을 가진 초기화되지 않은 객체들 // 모든 C++ 버전: 불확정 값 + 정의되지 않은 동작 int f(bool b) { unsigned char* c = new unsigned char; unsigned char d = *c; // OK, "d"는 불확정 값을 가짐 int e = d; // 정의되지 않은 동작 return b ? d : 0; // "b"가 true일 경우 정의되지 않은 동작 } // Case 2: 자동 저장 기간을 가진 초기화되지 않은 객체들 // C++26 이전: 불확정 값 + 정의되지 않은 동작 // C++26 이후: 오류 값 + 오류 동작 int g(bool b) { unsigned char c; // "c"는 불확정/오류 값을 가짐 unsigned char d = c; // 정의되지 않은/오류 동작 없음, // 하지만 "d"는 불확정/오류 값을 가짐 assert(c == d); // 성립하지만, 두 정수 승격 모두 // 정의되지 않은/오류 동작을 가짐 int e = d; // 정의되지 않은/오류 동작 return b ? d : 0; // "b"가 true일 경우 정의되지 않은/오류 동작 } // Case 2와 동일 void h() { int d1, d2; // "d1"과 "d2"는 불확정/오류 값을 가짐 int e1 = d1; // 정의되지 않은/오류 동작 int e2 = d1; // 정의되지 않은/오류 동작 assert(e1 == e2); // 성립 assert(e1 == d1); // 성립, 정의되지 않은/오류 동작 assert(e2 == d1); // 성립, 정의되지 않은/오류 동작 // 정의되지 않은/오류 동작 없음, // 하지만 "d2"는 불확정/오류 값을 가짐 std::memcpy(&d2, &d1, sizeof(int)); assert(e1 == d2); // 성립, 정의되지 않은/오류 동작 assert(e2 == d2); // 성립, 정의되지 않은/오류 동작 }
참고 사항
참조와 const 스칼라 객체는 기본 초기화될 수 없습니다.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_constexpr
|
201907L
|
(C++20) | 사소한 기본 초기화 및 asm-선언 in constexpr functions |
예제
#include <string> struct T1 { int mem; }; struct T2 { int mem; T2() {} // "mem"이 초기화 목록에 없음 }; int n; // 정적 비클래스, 2단계 초기화 수행: // 1) 0 초기화가 n을 0으로 초기화 // 2) 기본 초기화는 아무 작업도 하지 않아 n이 0으로 유지됨 int main() { [[maybe_unused]] int n; // 비클래스, 값은 불확정 std::string s; // 클래스, 기본 생성자 호출, 값은 "" std::string a[2]; // 배열, 요소들을 기본 초기화, 값은 {"", ""} // int& r; // 오류: 참조 // const int n; // 오류: const 비클래스 // const T1 t1; // 오류: 암시적 기본 생성자를 가진 const 클래스 [[maybe_unused]] T1 t1; // 클래스, 암시적 기본 생성자 호출 const T2 t2; // const 클래스, 사용자 제공 기본 생성자 호출 // t2.mem은 기본 초기화됨 }
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 178 | C++98 |
값 초기화가 없었음;
빈 초기화자는 기본 초기화를 수행함 (단, new T ( ) 는 0 초기화도 수행함) |
빈 초기화자는
값 초기화를 수행함 |
| CWG 253 | C++98 |
const 객체의 기본 초기화가
암시적으로 선언된 기본 생성자를 호출할 수 없었음 |
모든 하위 객체가 초기화된 경우 허용됨 |
| CWG 616 | C++98 |
초기화되지 않은 모든 객체의
lvalue에서 rvalue로의 변환이 항상 UB였음 |
indeterminate unsigned char 는 허용됨 |
| CWG 1787 | C++98 |
indeterminate
unsigned
char
를 레지스터에 캐시하여 읽는 것이 UB였음 |
well-defined로 변경됨 |