메인 콘텐츠로 건너뛰기

c++ 한 번에 초기화하기: 유니폼 초기화 (Uniform Initialization)

C++은 역사가 길다 보니 무언가를 초기화하는 방법이 너무 많았습니다. ()를 쓰기도 하고, =을 쓰기도 했죠. 모던 C++은 중괄호 {} 하나로 통일하는 것을 권장합니다.

2026년 1월 7일
c++
c++ 한 번에 초기화하기: 유니폼 초기화 (Uniform Initialization) - 아티클 커버 이미지

한때 프로그래밍의 세계는 서로 다른 도량형이 혼재하던 고대 시장과도 같았습니다. 어떤 물건은 무게로 재고 어떤 물건은 부피로 재듯, C++에서 변수를 초기화하는 방식 또한 제각각이었죠. 소괄호를 쓰기도 하고 등호를 쓰기도 하며 때로는 중괄호를 동원해야 했습니다. 이러한 파편화된 문법은 개발자들에게 끊임없는 혼란을 주었고 때로는 치명적인 버그의 온상이 되기도 했습니다. 하지만 C++11이라는 전환점을 지나며 우리는 마침내 유니폼 초기화라는 표준화된 자를 갖게 되었습니다. 모든 것을 중괄호 하나로 통일하려는 이 시도는 단순한 편의를 넘어 언어의 철학적 완성도를 높이게 되었죠.

데이터의 안전벨트 - Narrowing Conversion 방지

유니폼 초기화가 가져온 가장 혁신적인 변화는 데이터 손실에 대한 컴파일러의 태도 변화입니다. 과거의 대입 방식은 큰 데이터를 작은 그릇에 담을 때 발생하는 소수점 유실을 묵인하곤 했습니다. 이는 마치 거대한 조각상을 좁은 상자에 넣기 위해 팔다리를 잘라내는 것과 같았지만 컴파일러는 아무런 경고도 해주지 않았죠. 하지만 중괄호를 사용하는 순간 컴파일러는 파수꾼으로 돌변합니다.

// 기존 방식 (C-style)
int a = 3.9;  // 컴파일 가능하지만 소수점이 유실되어 3이 저장됩니다.

// 유니폼 초기화
int b{3.9};   // 컴파일 에러가 발생하며 데이터 손실 위험을 경고합니다.

감이 잘 오시지 않을 수 있지만 이 작은 차이가 새벽녘의 피곤한 개발자를 구원합니다. 의도치 않은 데이터 손실을 원천적으로 봉쇄함으로써 런타임에 발생할 정밀도 문제를 빌드 단계에서 해결해 주는 것이죠. 여러분은 그저 중괄호를 쓰는 것만으로도 강력한 안전장치를 하나 얻게 되는 셈입니다.

모호함의 안개를 걷어내는 구문 분석의 명료함

C++ 입문자들을 가장 당혹스럽게 만드는 현상 중 하나는 객체를 생성하려 했는데 컴파일러가 이를 함수 선언으로 오해하는 상황입니다. 이를 전문 용어로 가장 짜증 나는 구문 분석이라고 부르기도 하죠. 소괄호를 사용한 초기화는 문법적 모호함 때문에 컴파일러를 혼란에 빠뜨리곤 했습니다.

struct Timer {
    Timer() {} // 기본 생성자
};

// 1. 기존 방식의 문제
Timer t1(); // 컴파일러는 이를 "Timer를 반환하는 t1이라는 함수 선언"으로 오해합니다!

// 유니폼 초기화의 명확함
Timer t2{}; // 무조건 "Timer 객체 t2를 생성하라"는 뜻으로 해석됩니다.

과연 우리는 왜 이런 모호함 속에서 고통받아야 했을까요? 유니폼 초기화는 이러한 역사적 부채를 청산하려는 시도입니다. 어떤 상황에서도 흔들리지 않는 명확한 의사표시를 가능하게 함으로써 코드의 가독성과 안정성을 동시에 확보합니다.

만능 열쇠와 같은 일관된 문법의 매력

유니폼이라는 이름이 붙은 이유는 명확합니다. 일반 변수부터 배열, 구조체, 그리고 복잡한 컨테이너까지 모두 동일한 문법으로 다룰 수 있기 때문입니다. 이는 마치 전 세계 어디서나 통용되는 공용어를 익히는 것과 같습니다.

대상기존 방식유니폼 초기화
일반 변수int x = 10;int x{10};
배열int arr[] = {1, 2};int arr[]{1, 2};
구조체Point p = {1, 2};Point p{1, 2};
벡터복잡한 방식 필요std::vector v{1, 2, 3};

표에서 볼 수 있듯이 문법의 일관성은 학습 곡선을 낮추고 코드의 형태를 정돈해 줍니다. 여러분이 어떤 객체를 다루든 중괄호라는 하나의 도구만으로 해결할 수 있다는 사실은 심리적인 안정감마저 제공합니다.

경계해야 할 지점 초기화 리스트의 함정

물론 유니폼 초기화가 모든 문제를 해결하는 마법의 지팡이는 아닙니다. 기술의 빛이 강렬할수록 그 이면의 그림자도 살필 줄 알아야 하죠. 가장 대표적인 예외 상황은 std::initializer_list를 인자로 받는 생성자가 존재할 때입니다.

// std::vector의 초기화 차이
std::vector<int> v1(10, 2); // 2라는 숫자를 10개 담은 벡터 (크기 10)
std::vector<int> v2{10, 2}; // 10과 2라는 숫자를 직접 담은 벡터 (크기 2)

중괄호를 사용하면 컴파일러는 이를 데이터의 나열로 해석하려는 경향이 강합니다. 생성자의 의도가 객체의 설정값인지 아니면 담길 데이터의 목록인지에 따라 결과가 판이하게 달라질 수 있는 것이죠. 이는 기술적 편리함 뒤에 숨겨진 세밀한 규칙을 이해해야 하는 이유이기도 합니다.

결국 중요한 것은 실수를 줄이는 태도

모던 C++의 수많은 기능은 결국 인간은 실수하기 쉽다는 겸손한 인정에서 출발했습니다. 유니폼 초기화 역시 성능을 드라마틱하게 높여주는 기능은 아닐지도 모릅니다. 하지만 예기치 못한 버그로부터 시스템을 보호하고 코드를 더욱 우아하게 정렬해 주는 힘을 지니고 있습니다. 옛날 방식이 익숙하다는 이유로 새로운 도구를 멀리하고 있지는 않으신가요?

어쩌면 우리가 진정으로 고민해야 할 것은 문법의 암기가 아니라 그 문법이 설계된 의도를 읽어내는 일일 것입니다. 새벽 3시의 피로 속에서도 여러분의 코드를 지켜줄 중괄호의 힘을 믿어보는 것은 어떨까요?

공유하기