Namespaces
Variants

List-initialization (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

객체를 중괄호로 둘러싸인 초기화자 리스트 로부터 초기화합니다.

목차

구문

직접 목록 초기화

T 객체 { arg1, arg2, ... };

T 객체 {. des1 = arg1 , . des2 { arg2 } ... };

(C++20부터)
(1)
T { arg1, arg2, ... }

T {. des1 = arg1 , . des2 { arg2 } ... }

(C++20부터)
(2)
new T { arg1, arg2, ... }

new T {. des1 = arg1 , . des2 { arg2 } ... }

(C++20부터)
(3)
클래스 { T 멤버 { arg1, arg2, ... }; };

클래스 { T 멤버 {. des1 = arg1 , . des2 { arg2 } ... }; };

(C++20부터)
(4)
클래스 :: 클래스 () : 멤버 { arg1, arg2, ... } {...

클래스 :: 클래스 () : 멤버 {. des1 = arg1 , . des2 { arg2 } ... } {...

(C++20부터)
(5)

복사 목록 초기화

T 객체 = { 인수1, 인수2, ... };

T 객체 = {. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... };

(C++20부터)
(6)
함수 ({ 인수1, 인수2, ... })

함수 ({. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... })

(C++20부터)
(7)
return { 인수1, 인수2, ... };

return {. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... };

(C++20부터)
(8)
객체 [{ 인수1, 인수2, ... }]

객체 [{. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... }]

(C++20부터)
(9)
객체 = { 인수1, 인수2, ... }

객체 = {. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... }

(C++20부터)
(10)
U ({ 인수1, 인수2, ... })

U ({. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... })

(C++20부터)
(11)
클래스 { T 멤버 = { 인수1, 인수2, ... }; };

클래스 { T 멤버 = {. 지정자1 = 인수1 , . 지정자2 { 인수2 } ... }; };

(C++20부터)
(12)

목록 초기화는 다음과 같은 상황에서 수행됩니다:

  • 직접 목록 초기화 (명시적 및 비명시적 생성자 모두 고려됨)
1) 중괄호로 둘러싸인 초기화 리스트를 사용한 명명된 변수의 초기화
2) 중괄호로 둘러싸인 초기화 리스트를 사용한 이름 없는 임시 객체의 초기화
3) 동적 저장 기간을 가진 객체의 초기화로 new-expression 을 사용하며, 이때 초기화자는 중괄호로 둘러싸인 초기화자 리스트인 경우
4) 등호를 사용하지 않는 비정적 데이터 멤버 초기화 에서
5) 생성자의 member initializer list 에서 중괄호로 둘러싸인 초기화 리스트가 사용되는 경우
  • 복사 목록 초기화 (명시적 및 비명시적 생성자 모두 고려되지만, 비명시적 생성자만 호출될 수 있음)
6) 등호 뒤에 중괄호로 둘러싸인 초기화 리스트를 사용하여 명명된 변수의 초기화
7) 함수 호출 표현식에서, 인수로 사용된 중괄호로 둘러싸인 초기화 리스트와 함께, 그리고 리스트 초기화가 함수 매개변수를 초기화하는 경우
8) 중괄호로 둘러싸인 초기화 리스트가 반환 표현식으로 사용된 return 문에서 그리고 리스트 초기화가 반환된 객체를 초기화하는 경우
9) 사용자 정의 operator[] 를 사용하는 첨자 표현식 에서, 목록 초기화가 오버로드된 연산자의 매개변수를 초기화하는 경우
10) 할당 표현식 에서, 리스트 초기화가 오버로드된 연산자의 매개변수를 초기화하는 경우
11) 함수형 캐스트 표현식 또는 생성자 인수 대신 중괄호로 둘러싸인 초기화자 목록이 사용되는 다른 생성자 호출. 복사 목록 초기화는 생성자의 매개변수를 초기화합니다 (참고: 이 예제에서 U 유형은 목록 초기화되는 유형이 아닙니다; U 의 생성자 매개변수가 목록 초기화됩니다)
12) 등호를 사용하는 비정적 data member initializer 에서

설명

(cv 한정자가 있을 수 있는) T 타입 객체의 목록 초기화 효과는 다음과 같습니다:

  • 중괄호로 둘러싸인 초기화 목록에 지정 초기화 목록 이 포함되어 있고 T 가 참조 타입이 아닌 경우, T 는 집합체 클래스여야 합니다. 지정 초기화 목록의 지정자에 있는 정렬된 식별자들은 T 의 직접 비정적 데이터 멤버들의 정렬된 식별자들의 부분 수열을 형성해야 합니다. 집합체 초기화 가 수행됩니다.
(C++20부터)
  • 만약 T 가 집합 클래스이고 중괄호로 둘러싸인 초기화 리스트 (지정 초기화 리스트를 포함하지 않는) (C++20부터) 가 동일하거나 파생된 타입(possibly cv-qualified)의 단일 초기화 절을 가지고 있는 경우, 객체는 해당 초기화 절로부터 초기화됩니다 ( 복사 초기화 는 복사-리스트-초기화용, 직접 초기화 는 직접-리스트-초기화용).
  • 그렇지 않고 만약 T 가 문자 배열이고 중괄호로 둘러싸인 초기화 리스트가 적절한 타입의 문자열 리터럴인 단일 초기화 절을 가지고 있는 경우, 배열은 일반적으로 문자열 리터럴로부터 초기화됩니다 .
  • 그렇지 않고, 중괄호로 둘러싸인 초기화자 목록이 비어 있으며 T 가 기본 생성자를 가진 클래스 타입인 경우, 값 초기화(value-initialization) 가 수행됩니다.
  • 그렇지 않고 T 가 클래스 타입인 경우, T 의 생성자들을 두 단계로 고려합니다:
  • 이전 단계에서 일치하는 생성자를 찾지 못한 경우, T 의 모든 생성자들이 오버로드 해결 에 참여하며, 중괄호로 둘러싸인 초기화 리스트의 초기화 절들로 구성된 인수 집합에 대해 비축소 변환만 허용된다는 제한이 적용됩니다. 이 단계에서 복사-리스트-초기화에 대한 최적 일치로 명시적 생성자가 선택되면 컴파일이 실패합니다 (단순 복사-초기화에서는 명시적 생성자가 전혀 고려되지 않는다는 점에 유의).
  • 그렇지 않고, T 가 고정된 기반 타입 U 를 가진 열거형 타입 이고, 중괄호로 둘러싸인 초기화자 목록에 단일 초기화자 v 만 있으며 다음 조건들이 모두 충족되면, 열거형은 v U 로 변환한 결과로 초기화됩니다:
    • 초기화가 직접 목록 초기화(direct-list-initialization)인 경우.
    • v 스칼라 타입 인 경우.
    • v U 로 암시적으로 변환 가능한 경우.
    • v 에서 U 로의 변환이 축소 변환(narrowing)이 아닌 경우.
(C++17부터)
  • 그렇지 않은 경우 (즉 T 가 클래스 타입이 아닌 경우), 중괄호로 둘러싸인 초기화자 목록에 단 하나의 초기화 절만 있고, T 가 참조 타입이 아니거나 참조 타입인 경우 그 참조 대상 타입이 초기화 절의 타입과 동일하거나 해당 타입의 기본 클래스인 경우, T 직접 초기화 (직접 목록 초기화에서) 또는 복사 초기화 (복사 목록 초기화에서)되지만, 축소 변환은 허용되지 않습니다.
  • 그렇지 않고, T 가 초기화 절의 타입과 호환되지 않는 참조 타입인 경우:
  • T 가 참조하는 타입의 prvalue 임시 객체가 copy-list-initialized되고, 참조가 해당 임시 객체에 바인딩됩니다(참조가 non-const lvalue reference인 경우 실패합니다).
(C++17까지)
  • prvalue가 생성됩니다. prvalue는 copy-list-initialization으로 자신의 result object를 초기화합니다. 그런 다음 prvalue는 참조를 direct-initialize하는 데 사용됩니다(참조가 non-const lvalue reference인 경우 실패합니다). 임시 객체의 타입은 T 가 참조하는 타입입니다. , 단 T 가 "reference to array of unknown bound of U "인 경우, 임시 객체의 타입은 선언문 U x [ ] H 에서 x 의 타입이며, 여기서 H 는 initializer list입니다. (C++20부터) .
(C++17부터)
  • 그렇지 않고, 중괄호로 둘러싸인 초기화자 목록에 초기화 절이 없는 경우, T value-initialized 됩니다.

목록 초기화 std::initializer_list

std:: initializer_list < E > 타입의 객체는 초기화 리스트로부터 마치 컴파일러가 그리고 materialized (C++17부터) N const E 배열” 타입의 prvalue 를 생성한 것처럼 구성됩니다. 여기서 N 은 초기화 리스트에 있는 초기화 절의 개수입니다. 이를 초기화 리스트의 backing array (백킹 배열)이라고 합니다.

백킹 배열의 각 요소는 초기화 리스트의 해당 초기화 절에 의해 copy-initialized 되며, std:: initializer_list < E > 객체는 해당 배열을 참조하도록 구성됩니다. 복사에 선택된 생성자 또는 변환 함수는 초기화 리스트의 컨텍스트에서 accessible 해야 합니다. 요소 중 하나를 초기화하는 데 narrowing conversion이 필요한 경우 프로그램은 잘못된 형식입니다.

백킹 배열(backing array)은 다른 임시 객체(temporary object) 와 동일한 수명을 가지지만, std::initializer_list 객체를 백킹 배열로부터 초기화하는 경우, 배열의 수명은 임시 객체에 대한 참조를 바인딩(binding a reference to a temporary) 할 때와 정확히 동일하게 연장됩니다.

void f(std::initializer_list<double> il);
void g(float x)
{
   f({1, x, 3});
}
void h()
{
   f({1, 2, 3});
}
struct A { mutable int i; };
void q(std::initializer_list<A>);
void r()
{
    q({A{1}, A{2}, A{3}});
}
// 위의 초기화는 아래와 거의 동등한 방식으로 구현될 것입니다,
// 컴파일러가 initializer_list 객체를 한 쌍의 포인터로 구성할 수 있다고 가정하고,
// `__b`가 `f` 호출보다 오래 지속되지 않는다는 이해를 바탕으로 합니다.
void g(float x)
{
    const double __a[3] = {double{1}, double{x}, double{3}}; // backing array
    f(std::initializer_list<double>(__a, __a + 3));
}
void h()
{
    static constexpr double __b[3] =
        {double{1}, double{2}, double{3}}; // backing array
    f(std::initializer_list<double>(__b, __b + 3));
}
void r()
{
    const A __c[3] = {A{1}, A{2}, A{3}}; // backing array
    q(std::initializer_list<A>(__c, __c + 3));
}

모든 백킹 배열이 구별되는지(즉, 겹치지 않는 객체 에 저장되는지) 여부는 명시되어 있지 않습니다:

bool fun(std::initializer_list<int> il1, std::initializer_list<int> il2)
{
    return il2.begin() == il1.begin() + 1;
}
bool overlapping = fun({1, 2, 3}, {2, 3, 4}); // 결과는 명시되지 않음:
                                              // 내부 배열이 {1, 2, 3, 4} 내에서
                                              // 저장 공간을 공유할 수 있음

축소 변환

목록 초기화는 다음과 같은 변환을 금지하여 허용되는 암시적 변환 을 제한합니다:

  • 부동 소수점 타입에서 정수 타입으로의 변환
  • 부동 소수점 타입 T 에서 다른 부동 소수점 타입으로의 변환. 단, 대상 타입의 floating-point conversion rank T 의 랭크보다 크지도 않고 같지도 않은 경우. 다음 조건 중 하나를 만족하는 상수 표현식인 경우는 예외:
    • 변환된 값이 유한하며 변환 시 오버플로우가 발생하지 않는 경우
    • 변환 전과 변환 후의 값이 모두 비유한 값인 경우
  • 정수형에서 부동소수점형으로의 변환, 단 원본이 대상 타입에 정확히 저장될 수 있는 상수 표현식인 경우는 제외
  • 정수형 또는 비범위 열거형에서 원본의 모든 값을 표현할 수 없는 정수형으로의 변환. 단, 다음의 경우는 제외:
    • 소스가 비트 필드 이며 그 너비 w 가 해당 타입(또는 열거형 의 경우 기반 타입)의 너비보다 작고, 대상 타입이 가상의 확장 정수형(너비 w 이며 원본 타입과 동일한 부호 특성을 가짐)의 모든 값을 표현할 수 있는 경우, 또는
    • 소스가 상수 표현식이며 그 값을 대상 타입에 정확히 저장할 수 있는 경우
  • 포인터 타입 또는 포인터-대-멤버 타입에서 bool 로의 변환

참고 사항

모든 초기화 절은 sequenced before 중괄호로 둘러싸인 초기화 목록에서 그 뒤에 오는 어떤 초기화 절보다 먼저 순서가 정해집니다. 이는 function call expression 의 인수들과 대조되는데, 인수들은 unsequenced (until C++17) indeterminately sequenced (since C++17) 입니다.

중괄호로 둘러싸인 초기화 리스트는 표현식이 아니므로 타입을 가지지 않습니다. 예를 들어 decltype ( { 1 , 2 } ) 는 잘못된 형식입니다. 타입이 없다는 것은 템플릿 타입 추론이 중괄호로 둘러싸인 초기화 리스트와 일치하는 타입을 추론할 수 없음을 의미합니다. 따라서 선언 template < class T > void f ( T ) ; 가 주어졌을 때, 표현식 f ( { 1 , 2 , 3 } ) 는 잘못된 형식입니다. 그러나 템플릿 매개변수는 다른 경우에는 추론될 수 있습니다. 예를 들어 std:: vector < int > v ( std:: istream_iterator < int > ( std:: cin ) , { } ) 와 같은 경우, 반복자 타입이 첫 번째 인수로 추론되지만 두 번째 매개변수 위치에서도 사용됩니다. 특별한 예외로 auto 키워드를 사용한 타입 추론 이 있으며, 이는 복사-리스트-초기화에서 모든 중괄호로 둘러싸인 초기화 리스트를 std::initializer_list 로 추론합니다.

또한 중괄호로 둘러싸인 초기화 리스트는 타입을 가지지 않기 때문에, 오버로드 해결을 위한 특별한 규칙들 이 오버로드된 함수 호출의 인수로 사용될 때 적용됩니다.

집합체는 동일한 타입의 단일 초기화 절을 가진 중괄호로 둘러싸인 초기화 목록에서 직접 복사/이동 초기화를 수행하지만, 비집합체는 std::initializer_list 생성자를 먼저 고려합니다:

struct X {}; // 집합체(aggregate)
struct Q     // 비집합체(non-aggregate)
{
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};
int main()
{
    X x;
    X x2 = X{x}; // 복사 생성자 (집합체 초기화가 아님)
    Q q;
    Q q2 = Q{q}; // 초기화 리스트 생성자 (복사 생성자가 아님)
}

일부 컴파일러(예: gcc 10)는 C++20 모드에서 포인터나 멤버 포인터에서 bool 로의 변환만 축소 변환으로 간주합니다.

기능 테스트 매크로 표준 기능
__cpp_initializer_lists 200806L (C++11) 목록 초기화 및 std::initializer_list

예제

#include <iostream>
#include <map>
#include <string>
#include <vector>
struct Foo
{
    std::vector<int> mem = {1, 2, 3}; // 비정적 멤버의 목록 초기화
    std::vector<int> mem2;
    Foo() : mem2{-1, -2, -3} {} // 생성자에서 멤버의 목록 초기화
};
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first}; // 반환문에서의 목록 초기화
}
int main()
{
    int n0{};  // 값 초기화(0으로)
    int n1{1}; // 직접 목록 초기화
    std::string s1{'a', 'b', 'c', 'd'}; // initializer-list 생성자 호출
    std::string s2{s1, 2, 2};           // 일반 생성자 호출
    std::string s3{0x61, 'a'}; // initializer-list 생성자가 (int, char)보다 우선됨
    int n2 = {1}; // 복사 목록 초기화
    double d = double{1.2}; // prvalue의 목록 초기화, 이후 복사 초기화
    auto s4 = std::string{"HelloWorld"}; // 위와 동일, C++17부터는 임시 객체 생성되지 않음
                                         // created since C++17
    std::map<int, std::string> m = // 중첩 목록 초기화
    {
        {1, "a"},
        {2, {'a', 'b', 'c'}},
        {3, s1}
    };
    std::cout << f({"hello", "world"}).first // 함수 호출에서의 목록 초기화
              << '\n';
    const int (&ar)[2] = {1, 2}; // 임시 배열에 lvalue 참조를 바인딩
    int&& r1 = {1}; // 임시 int에 rvalue 참조를 바인딩
//  int& r2 = {2}; // 오류: rvalue를 비 const lvalue 참조에 바인딩할 수 없음
//  int bad{1.0}; // 오류: 축소 변환
    unsigned char uc1{10}; // 정상
//  unsigned char uc2{-1}; // 오류: 축소 변환
    Foo f;
    std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
              << s1 << ' ' << s2 << ' ' << s3 << '\n';
    for (auto p : m)
        std::cout << p.first << ' ' << p.second << '\n';
    for (auto n : f.mem)
        std::cout << n << ' ';
    for (auto n : f.mem2)
        std::cout << n << ' ';
    std::cout << '\n';
    [](...){}(d, ar, r1, uc1); // [[maybe_unused]]의 효과를 가짐
}

출력:

world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 수정된 동작
CWG 1288 C++11 단일 초기화 절을 가진 중괄호 초기화 목록으로 참조를 목록 초기화할 때
항상 임시 객체에 바인딩됨
유효한 경우 해당 초기화 절에
바인딩
CWG 1290 C++11 백업 배열의 수명이 올바르게 명시되지 않음 다른 임시 객체와 동일하게
명시
CWG 1324 C++11 {} 로부터의 초기화에서 초기화를 먼저 고려함 집계 초기화를
먼저 고려
CWG 1418 C++11 백업 배열의 타입에 const 가 없음 const 추가됨
CWG 1467 C++11 동일 타입 집계 및 문자 배열 초기화가 금지됨; 단일 절 목록에서
초기화 목록 생성자가 복사 생성자보다 우선순위를 가짐
동일 타입 초기화
허용; 단일 절 목록은
직접 초기화
CWG 1494 C++11 호환되지 않는 타입의 초기화 절로 참조를 목록 초기화할 때
생성된 임시 객체가 직접 목록 초기화되는지 복사 목록 초기화되는지
명시되지 않음
참조에 대한 초기화
종류에 따라 결정됨
CWG 2137 C++11 {X} 로부터 X 를 목록 초기화할 때 초기화 목록 생성자가
복사 생성자에 밀림
비집계는 초기화 목록을
먼저 고려
CWG 2252 C++17 열거형이 비스칼라 값으로부터 목록 초기화될 수 있음 금지됨
CWG 2267 C++11 CWG 이슈 1494 의 해결책이
임시 객체가 직접 목록 초기화될 수 있음을 명확히 함
참조 목록 초기화 시
복사 목록 초기화됨
CWG 2374 C++17 열거형의 직접 목록 초기화에서 너무 많은 소스 타입이 허용됨 제한됨
CWG 2627 C++11 더 큰 정수 타입의 좁은 비트 필드는 더 작은 정수 타입으로
승격될 수 있지만 여전히 축소 변환이었음
축소 변환이
아님
CWG 2713 C++20 집계 클래스에 대한 참조를 지정 초기화 목록으로
초기화할 수 없음
허용됨
CWG 2830 C++11 목록 초기화가 최상위 cv-한정자를 무시하지 않음 무시함
CWG 2864 C++11 오버플로우되는 부동소수점 변환이 축소 변환이 아니었음 축소 변환임
P1957R2 C++11 포인터/멤버 포인터에서 bool 로의 변환이
축소 변환이 아니었음
축소 변환으로 간주됨
P2752R3 C++11 수명이 겹치는 백업 배열이 겹칠 수 없었음 겹칠 수 있음

참고 항목