[2026] C++ 초기화 리스트 생성자 | Initializer List 가이드
이 글의 핵심
C++ 초기화 리스트 생성자의 핵심 개념과 실무 포인트를 정리합니다.
초기화 리스트 생성자란?
std::initializer_list를 받는 생성자
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
#include <vector>
class MyVector {
private:
std::vector<int> data;
public:
// 초기화 리스트 생성자
MyVector(std::initializer_list<int> list) {
for (int x : list) {
data.push_back(x);
}
}
};
int main() {
MyVector vec = {1, 2, 3, 4, 5}; // 초기화 리스트 사용
}
기본 사용법
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
#include <iostream>
class IntArray {
private:
int* data;
size_t size;
public:
IntArray(std::initializer_list<int> list)
: size(list.size()), data(new int[list.size()]) {
size_t i = 0;
for (int x : list) {
data[i++] = x;
}
}
~IntArray() {
delete[] data;
}
void print() const {
for (size_t i = 0; i < size; i++) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
};
int main() {
IntArray arr = {1, 2, 3, 4, 5};
arr.print(); // 1 2 3 4 5
}
표준 컨테이너와 함께
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <vector>
#include <map>
#include <set>
int main() {
// vector
std::vector<int> vec = {1, 2, 3, 4, 5};
// map
std::map<std::string, int> ages = {
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35}
};
// set
std::set<int> numbers = {5, 2, 8, 1, 9};
}
실전 예시
예시 1: 행렬 클래스
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
#include <vector>
class Matrix {
private:
std::vector<std::vector<int>> data;
size_t rows, cols;
public:
// 2D 초기화 리스트
Matrix(std::initializer_list<std::initializer_list<int>> list) {
rows = list.size();
cols = list.begin()->size();
for (const auto& row : list) {
data.push_back(std::vector<int>(row));
}
}
void print() const {
for (const auto& row : data) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
};
int main() {
Matrix mat = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
mat.print();
}
예시 2: 설정 클래스
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
#include <map>
#include <string>
class Config {
private:
std::map<std::string, std::string> settings;
public:
Config(std::initializer_list<std::pair<const std::string, std::string>> list) {
for (const auto& pair : list) {
settings[pair.first] = pair.second;
}
}
std::string get(const std::string& key) const {
auto it = settings.find(key);
return it != settings.end() ? it->second : "";
}
void print() const {
for (const auto& [key, value] : settings) {
std::cout << key << " = " << value << std::endl;
}
}
};
int main() {
Config config = {
{"host", "localhost"},
{"port", "8080"},
{"debug", "true"}
};
config.print();
}
예시 3: 가변 인자 함수
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
int sum(std::initializer_list<int> list) {
int total = 0;
for (int x : list) {
total += x;
}
return total;
}
double average(std::initializer_list<double> list) {
if (list.size() == 0) return 0.0;
double total = 0.0;
for (double x : list) {
total += x;
}
return total / list.size();
}
int main() {
std::cout << sum({1, 2, 3, 4, 5}) << std::endl; // 15
std::cout << average({1.5, 2.5, 3.5}) << std::endl; // 2.5
}
예시 4: 빌더 패턴
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
#include <string>
#include <vector>
class QueryBuilder {
private:
std::string table;
std::vector<std::string> columns;
std::string whereClause;
public:
QueryBuilder& from(const std::string& t) {
table = t;
return *this;
}
QueryBuilder& select(std::initializer_list<std::string> cols) {
columns = cols;
return *this;
}
QueryBuilder& where(const std::string& condition) {
whereClause = condition;
return *this;
}
std::string build() const {
std::string query = "SELECT ";
for (size_t i = 0; i < columns.size(); i++) {
query += columns[i];
if (i < columns.size() - 1) query += ", ";
}
query += " FROM " + table;
if (!whereClause.empty()) {
query += " WHERE " + whereClause;
}
return query;
}
};
int main() {
auto query = QueryBuilder()
.from("users")
.select({"id", "name", "email"})
.where("age > 18")
.build();
std::cout << query << std::endl;
// SELECT id, name, email FROM users WHERE age > 18
}
우선순위
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class MyClass {
public:
MyClass(int x) {
std::cout << "int 생성자: " << x << std::endl;
}
MyClass(std::initializer_list<int> list) {
std::cout << "initializer_list 생성자" << std::endl;
}
};
int main() {
MyClass obj1(10); // int 생성자
MyClass obj2{10}; // initializer_list 생성자 (우선!)
MyClass obj3 = {10}; // initializer_list 생성자
}
자주 발생하는 문제
문제 1: 생성자 모호성
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class MyClass {
public:
MyClass(int x, int y) {
std::cout << "두 int 생성자" << std::endl;
}
MyClass(std::initializer_list<int> list) {
std::cout << "initializer_list 생성자" << std::endl;
}
};
int main() {
MyClass obj1(1, 2); // 두 int 생성자
MyClass obj2{1, 2}; // initializer_list 생성자 (우선!)
}
문제 2: narrowing 변환
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ narrowing 에러
std::initializer_list<int> list = {1, 2, 3.14}; // 에러
// ✅ 명시적 변환
std::initializer_list<int> list = {1, 2, static_cast<int>(3.14)};
문제 3: 빈 리스트
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class MyClass {
public:
MyClass() {
std::cout << "기본 생성자" << std::endl;
}
MyClass(std::initializer_list<int> list) {
std::cout << "initializer_list 생성자" << std::endl;
}
};
int main() {
MyClass obj1; // 기본 생성자
MyClass obj2{}; // 기본 생성자 (빈 리스트 아님!)
MyClass obj3{{}}; // initializer_list 생성자 (빈 리스트)
}
initializer_list 특징
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <initializer_list>
void func(std::initializer_list<int> list) {
// 읽기 전용
// list[0] = 10; // 에러
// 크기 확인
std::cout << "크기: " << list.size() << std::endl;
// 반복
for (int x : list) {
std::cout << x << " ";
}
}
int main() {
func({1, 2, 3, 4, 5});
}
initializer_list 동작 원리
std::initializer_list<T>는 컴파일러가 생성한 임시 배열을 가리키는 얇은 래퍼입니다. {1, 2, 3} 같은 브레이스 초기화가 발생하면, 구현은 보통 const T[] 형태의 저장소를 스택(또는 정적 저장 영역)에 두고, initializer_list는 그 배열의 시작 포인터와 길이만 담습니다.
- 수명: 리스트는 보통 함수 호출 인자로 전달되는 경우 호출이 끝날 때까지 유효합니다.
auto il = {1,2,3};처럼 지역 변수에 바인딩하면, 해당 변수가 스코프를 벗어나기 전까지 동일한 배열을 참조합니다. 다만initializer_list를 멤버로 오래 보관하면서 원본 배열 수명이 끝난 뒤 접근하면 UB이므로, 장기 보관이 필요하면vector등으로 복사하는 편이 안전합니다. - 복사 의미:
initializer_list객체를 복사해도 내부적으로는 같은 배열을 가리키는 포인터/크기를 공유하는 수준이라 가벼운 편입니다. 하지만 요소 자체의 복사는 초기화 시점에 이미 일어났을 수 있습니다(예:string요소). - const 뷰: 요소는
const T로 노출되며, 크기 변경·요소 대입은 불가능합니다. “가변 길이 배열”이 아니라 고정 길이 읽기 전용 뷰로 이해하면 됩니다. 다음은 간단한 cpp 코드 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
void take(std::initializer_list<int> il) {
// il.begin(), il.end(), il.size()
for (int x : il) { /* 읽기만 */ }
}
벡터 생성자와의 관계
std::vector는 대표적으로 (size, value), (iterator 쌍), initializer_list 생성자를 제공합니다. 여기서 {} 초기화는 규칙에 따라 initializer_list 쪽으로 해석될 수 있어, vector 문서에서 자주 경고하는 “두 요소짜리 리스트 vs (size, value)” 혼동과 연결됩니다.
- vector
v(10, 20) : 크기 10, 값 20. - vector
v{10, 20} : 요소 두 개인 리스트10, 20→initializer_list생성자. 커스텀 클래스에서도 일반 생성자와initializer_list생성자가 함께 있으면,{}는 후자를 우선 시도합니다(이 글 앞쪽 “우선순위” 절 참고). API 설계 시 “요소 나열”과 “두 스칼라 인자”를 동시에 두면 사용자가 혼동하기 쉬우므로,()와{}의 의미를 팀 규칙으로 정리하거나explicit·별도 팩토리 함수로 의도를 나누는 경우가 많습니다.vector에 리스트를 넣을 때 내부적으로는initializer_list범위로 한 번에 삽입하는 경로가 사용되며, 이미 길이를 아는 경우reserve후assign/insert와의 미세한 차이는 있지만, 대부분은 가독성과 “모호성 없는 호출 문법”이 더 큰 이슈입니다.
실전 패턴 보강
- API에서 “여러 값 나열”:
select({...}), 옵션 플래그 집합, 테스트 픽스처 구성 등에initializer_list를 쓰면 호출부가 간결해집니다. 다만 템플릿 추론과 섞이면 의도치 않은initializer_list선택이 나올 수 있어, 공개 API는 오버로드 최소화가 중요합니다. - 비용이 큰 타입:
initializer_list<string>처럼 요소가 비싼 타입이면, 호출부에서 임시 문자열이 여러 개 만들어질 수 있습니다. 이때는string_view기반 오버로드나span/vector를 받는 별도 API를 고려합니다. - 명시적 균일 초기화: 팀에서
()/{}규칙을 통일하면(예: 컨테이너는{}, 스칼라 변환은()), 리뷰 시 “모호한 중괄호” 논쟁을 줄일 수 있습니다.
성능 고려사항
- 할당:
initializer_list자체는 보통 추가 힙 할당 없이 배열을 가리킵니다. 그러나 이를vector에 넣는 과정에서 컨테이너 쪽 할당은 발생합니다. 크기를 미리 알면reserve로 재할당 횟수를 줄일 수 있습니다. - 이중 작업: 직접
initializer_list를 순회하며push_back만 반복하면,vector에 넘길 때와 비교해 불필요한 한 번 더 순회가 될 수 있습니다. 가능하면 범위 생성자/할당자를 사용하거나, 한 번의 순회로 필요한 용량을 계산합니다. - 인라인 배열: 요소가 많고 핫 루프에서 매번 리스트 리터럴을 만들면, 컴파일러·최적화에 따라 스택 사용과 캐시 영향이 달라질 수 있습니다. 이런 경우 정적
array/vector상수로 빼 두는 편이 측정상 유리한 경우가 있습니다.
FAQ
Q1: initializer_list는 언제 사용?
A:
- 컨테이너 초기화
- 가변 인자 함수
- 균일 초기화
Q2: 우선순위는?
A: initializer_list 생성자가 우선. {}는 항상 initializer_list 시도.
Q3: 성능은?
A: 경량 프록시. 복사 비용 낮음.
Q4: 수정 가능?
A: 불가. 읽기 전용.
Q5: 빈 리스트는?
A: {{}} 사용. {}는 기본 생성자.
Q6: initializer_list 학습 리소스는?
A:
- “Effective Modern C++”
- cppreference.com
- “C++ Primer”
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ initializer_list | “초기화 리스트” 가이드
- C++ 균일 초기화 | “Uniform Initialization” 가이드
- C++ explicit Keyword | “explicit 키워드” 가이드