주간 팁 #172: 지정 초기화자(Designated Initializers)
원래 TotW #172로 2019년 12월 11일 게시됨
작성자: Aaron Jacobs
2020-04-06 업데이트됨
빠른 링크: abseil.io/tips/172
지정 초기화자는 구조체의 내용을 간결하면서도 읽기 쉽고 유지보수 가능한 방식으로 지정하기 위한 C++20 표준의 문법입니다. 다음과 같은 반복적인 코드를 작성하는 대신:
struct Point {
double x;
double y;
double z;
};
Point point;
point.x = 3.0;
point.y = 4.0;
point.z = 5.0;
지정 초기화자를 사용하여 다음과 같이 작성할 수 있습니다:
Point point = {
.x = 3.0,
.y = 4.0,
.z = 5.0,
};
이 방식은 반복 작업을 줄이는 데 도움이 될 뿐만 아니라, 더 다양한 상황에서도 사용할 수 있습니다. 예를 들어, 구조체를 const
로 설정하여 번거로운 우회 작업 없이도 변경 불가능하게 만들 수 있습니다:
// 코드가 복잡한 경우 이 구조체가 절대 변경되지 않음을 독자에게 명확히 알립니다.
const Point character_position = { .x = 3.0 };
또는 추가 식별자를 범위 내에 도입하지 않고도 함수 호출에서 직접 사용할 수 있습니다:
std::vector<Point> points;
[...]
points.push_back(Point{.x = 3.0, .y = 3.0});
points.push_back(Point{.x = 4.0, .y = 4.0});
의미론
지정 초기화자는 집합 초기화의 한 형태로, 집합에서만 사용할 수 있습니다. 이는 “사용자 정의 생성자나 가상 함수가 없는 구조체 또는 클래스”를 의미하며, 일반적으로 Google 스타일에서는 class
대신 struct
를 사용할 때 해당됩니다.
C++20 지정 초기화자의 의미론은 생성자에서의 멤버 초기화 목록과 같은 다른 C++ 언어 기능을 기반으로 예상할 수 있는 동작을 합니다. 명시적으로 언급된 필드는 제공된 표현식으로 초기화되며, 기본 동작을 원할 경우 필드를 생략할 수 있습니다:
Point point = {
.x = 1.0,
// y는 0.0으로 설정됨
.z = 2.0,
};
위에서 “기본값”은 무엇을 의미할까요? 특별한 경우(예: union
)를 제외하고, 다음과 같습니다:
- 구조체 정의에 기본 멤버 초기화자가 포함된 경우(예:
std::string foo = "default value";
), 해당 값이 사용됩니다. - 그렇지 않으면 필드는
= {}
로 초기화된 것처럼 설정됩니다. 이는 기본 데이터 유형의 경우 0 값이 설정되고, 더 복잡한 클래스의 경우 기본 생성된 인스턴스를 얻는 것을 의미합니다.
이 동작은 일반적으로 가장 예상 가능한 결과를 제공합니다. 자세한 내용은 표준을 참조하세요.
역사와 언어 트리비아
지정 초기화자는 C99 이후 C 언어의 표준적인 일부였으며, 이전부터 컴파일러에서 비표준 확장으로 제공되었습니다. 하지만 최근까지는 C++의 일부가 아니었습니다. 이는 C가 C++의 부분 집합이 아니었던 주목할 만한 예입니다. 이로 인해 Google 스타일 가이드는 사용하지 말 것을 권장하기도 했습니다.
20년이 지나 상황이 바뀌었습니다. 이제 지정 초기화자는 C++20 표준의 일부가 되었습니다.
C++20의 지정 초기화자는 C 버전에 비해 몇 가지 제한이 있습니다:
- C++20에서는 필드가 구조체 정의에 나열된 순서대로 지정자에 나열되어야 합니다(따라서
Point{.y = 1.0, .x = 2.0}
는 유효하지 않음). C에서는 이를 요구하지 않습니다. - C에서는 지정된 초기화자와 지정되지 않은 초기화자를 혼합할 수 있지만(예:
Point{1.0, .z = 2.0}
), C++20에서는 이를 허용하지 않습니다. - C에서는 “배열 지정자(array designators)”로 알려진 희소 배열 초기화를 지원하지만, 이는 C++20의 일부가 아닙니다.