13. 초기화 문법의 함정 ({} vs (), initializer_list 우선, MVP, narrowing)
난이도 중내 리뷰 · C++
해설 · C++
해설 — 초기화 문법의 함정 ({} vs (), initializer_list 우선, MVP, narrowing)
난이도: 중상
요약
중괄호 {} 초기화는 후보 생성자 중 std::initializer_list를 받는 것을 강하게 우선한다. 그래서 같은 인자라도 ()와 {}의 결과가 달라진다. 실행 출력:
a.size=3 a[0]=7 // (a) vector(count=3, value=7) → 7,7,7
b.size=2 b[0]=3 // (b) initializer_list{3,7} → 3,7
c.size=3 c[0]=0 // (c) vector(count=3) → 0,0,0
d.size=1 d[0]=3 // (d) initializer_list{3} → 3
p=(1,2) q=(0,0) // (p) 집합체 초기화, (q) 값 초기화
s='xxxxx' len=5 // (s) string(count=5, ch='x')
t len=2 first=5 second=120 // (t) initializer_list<char>{5,'x'} → '\x05','x'
핵심 개념
중괄호 목록으로 초기화할 때 오버로드 해석은 2단계다:
std::initializer_list생성자만으로 먼저 시도. 목록 원소들이 그initializer_list의 원소 타입으로 변환 가능하면 그게 선택된다(다른 일반 생성자보다 우선).- 1단계 후보가 없을 때만 일반 생성자들로 다시 해석.
이 우선 규칙 때문에 "개수+값" 생성자가 가려진다.
단계별 내부 동작
- (a)
vector<int> a(3, 7): 소괄호 →initializer_list가 끼지 않는다.vector(size_type count, const T& value)호출 →{7,7,7}. size 3. - (b)
vector<int> b{3, 7}: 중괄호 →vector(std::initializer_list<int>{3,7})가 우선 → 원소3,7. size 2. - (c)
vector<int> c(3): 소괄호 →vector(count=3)→ 기본값 0 세 개. size 3. - (d)
vector<int> d{3}: 중괄호 →initializer_list<int>{3}우선 → 원소3하나. size 1. ((3)이었다면 0 세 개가 됐을 것 — 한 글자 차이로 의미가 완전히 달라진다.) - (p)
Point p{1,2}:Point는 사용자 정의 생성자가 없는 집합체(aggregate). 중괄호로 멤버를 순서대로 초기화 →x=1,y=2. - (q)
Point q{}: 빈 중괄호 → 값 초기화 → 모든 멤버 0 →(0,0). (Point q;였다면 지역 변수라 미초기화 쓰레기값.) - (s)
string s(5,'x'): 소괄호 →string(count=5, char='x')→"xxxxx". - (t)
string t{5,'x'}⚠️: 중괄호 →std::string은initializer_list<char>생성자가 있다.5는char로 변환되어'\x05'(값 5),'x'는 120. 즉 두 글자짜리 문자열"\x05x". "x가 5개"가 절대 아니다.
흔한 오해·함정
- most vexing parse (i):
Widget w();는 "기본 생성으로 만든 변수 w"가 아니라 인자 없는 함수 w의 선언이다.Widget w{};(또는Widget w;)로 써야 객체가 만들어진다.std::vector<int> v(std::istream_iterator<int>(cin), std::istream_iterator<int>())같은 형태도 함수 선언으로 파싱되는 고전 사례. - narrowing 금지 (ii):
int n{3.5};는double→int가 손실 변환이라 중괄호에서 컴파일 에러(narrowing 금지). 반면int n(3.5);는 조용히3으로 잘린다. 중괄호는 의도치 않은 축소를 컴파일 타임에 잡아준다 — 이건 함정이자 안전장치. - "
{}는 항상 더 안전/현대적이니 무조건 쓰자" →initializer_list우선 때문에vector/string같은 컨테이너에서는 의미가 바뀐다. "개수/크기"를 줄 땐(), "원소 목록"을 줄 땐{}로 의도를 구분해야 한다.
면접 포인트
{}가initializer_list생성자를 우선한다는 2단계 해석과,vector{3,7}vsvector(3,7)의 결과 차이.Point q{}(값 초기화, 0) vsPoint q;(미초기화)의 차이, 집합체 초기화 규칙.- most vexing parse가 무엇이고 어떻게 피하는지, 중괄호의 narrowing 금지가 주는 안전성.