Namespaces
Variants

Direct-initialization

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 객체 ( 인수 );

T 객체 ( 인수1, 인수2, ... );

(1)
T 객체 { 인수 }; (2) (C++11부터)
T ( 다른_객체 )

T ( 인수1, 인수2, ... )

(3)
static_cast< T >( 다른_객체 ) (4)
new T ( 인수들, ... ) (5)
클래스 :: 클래스 () : 멤버 ( 인수들, ... ) { ... } (6)
[ 인수 ]() { ... } (7) (C++11부터)

설명

직접 초기화(direct-initialization)는 다음과 같은 상황에서 수행됩니다:

1) 비어 있지 않은 괄호로 묶인 표현식 목록 또는 중괄호 초기화 목록 (C++11부터) 으로 초기화.
2) 단일 중괄호로 둘러싸인 초기화자를 사용한 비클래스 타입 객체의 초기화 (참고: 클래스 타입 및 중괄호 초기화 목록의 다른 사용법에 대해서는 list-initialization 참조) (C++11부터) .
3) prvalue 임시 객체의 초기화 (C++17까지) prvalue의 결과 객체 초기화 (C++17부터) 함수 방식 캐스트 또는 괄호로 묶인 표현식 목록으로 수행.
4) static_cast 표현식에 의한 prvalue 임시 객체의 초기화 (C++17 이전) prvalue의 결과 객체 초기화 (C++17 이후)
5) 초기화자를 포함한 new-표현식에 의한 동적 저장 기간 객체의 초기화.
6) 생성자 initializer list 를 통한 베이스 클래스 또는 비정적 멤버의 초기화.
7) 람다 표현식에서 복사로 캡처된 변수들로부터 클로저 객체 멤버들의 초기화.

직접 초기화(direct-initialization)의 효과는 다음과 같습니다:

  • 만약 T 가 배열 타입인 경우,
  • 프로그램이 형식에 맞지 않습니다.
(C++20 이전)
struct A
{
    explicit A(int i = 0) {}
};
A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A()
A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1]
              //        from {} selected explicit constructor
(C++20 이후)
  • 만약 T 가 클래스 타입이라면,
  • 초기화자가 prvalue 표현식이고 그 타입이 T 와 동일한 클래스인 경우(cv 한정자는 무시), 임시 객체로부터 물질화되는 대신 초기화자 표현식 자체가 대상 객체를 초기화하는 데 사용됩니다.
    (C++17 이전에는 컴파일러가 이 경우 prvalue 임시 객체로부터의 생성을 생략할 수 있었지만, 적절한 생성자는 여전히 접근 가능해야 합니다: copy elision 참조)
(C++17부터)
  • T 의 생성자들을 검토하고 오버로드 해결을 통해 가장 적합한 항목을 선택합니다. 그런 다음 생성자를 호출하여 객체를 초기화합니다.
  • 그렇지 않고 대상 타입이 (cv 한정자가 있을 수 있는) 집합 클래스인 경우, aggregate initialization 에 설명된 대로 초기화되지만, 축소 변환은 허용되고 지정 초기자는 허용되지 않으며, 참조에 바인딩된 임시 객체의 수명은 연장되지 않고, 중괄호 생략은 없으며, 초기화자가 없는 모든 요소는 value-initialized 됩니다.
struct B
{
    int a;
    int&& r;
};
int f();
int n = 10;
B b1{1, f()};            // OK, lifetime is extended
B b2(1, f());            // well-formed, but dangling reference
B b3{1.0, 1};            // error: narrowing conversion
B b4(1.0, 1);            // well-formed, but dangling reference
B b5(1.0, std::move(n)); // OK
(since C++20)
  • 그렇지 않고 T 가 비클래스 타입이지만 소스 타입이 클래스 타입인 경우, 소스 타입과 해당 기본 클래스들의 변환 함수들을 검사하고 오버로드 해결을 통해 가장 적합한 매치를 선택합니다. 선택된 사용자 정의 변환은 초기화 표현식을 초기화되는 객체로 변환하는 데 사용됩니다.
  • 그렇지 않고 T bool 이고 소스 타입이 std::nullptr_t 인 경우, 초기화된 객체의 값은 false 입니다.
  • 그렇지 않은 경우, 필요한 경우 표준 변환 을 사용하여 other 의 값을 T 의 cv-unqualified 버전으로 변환하며, 초기화되는 객체의 초기값은 (변환될 수 있는) 해당 값입니다.

참고 사항

직접 초기화(direct-initialization)는 복사 초기화(copy-initialization)보다 더 허용적입니다: 복사 초기화는 비- explicit 생성자와 비-explicit 사용자 정의 conversion functions 만 고려하는 반면, 직접 초기화는 모든 생성자와 모든 사용자 정의 변환 함수를 고려합니다.

직접 초기화 구문 (1) (둥근 괄호 사용)을 이용한 변수 선언과 함수 선언 사이에 모호성이 발생하는 경우, 컴파일러는 항상 함수 선언을 선택합니다. 이러한 모호성 해소 규칙은 때로 직관적이지 않으며 가장 성가신 구문 분석(most vexing parse) 로 불립니다.

#include <fstream>
#include <iterator>
#include <string>
int main()
{
    std::ifstream file("data.txt");
    // 다음은 함수 선언입니다:
    std::string foo1(std::istreambuf_iterator<char>(file),
                     std::istreambuf_iterator<char>());
    // 이는 foo1이라는 함수를 선언하며, 반환 타입은 std::string,
    // 첫 번째 매개변수는 std::istreambuf_iterator<char> 타입이고 이름이 "file"이며,
    // 두 번째 매개변수는 이름이 없고 std::istreambuf_iterator<char>() 타입으로,
    // 함수 포인터 타입 std::istreambuf_iterator<char>(*)()로 재작성됩니다
    // C++11 이전 해결책 (변수 선언) - 인수 중 하나에 추가 괄호 사용:
    std::string str1((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
    // C++11 이후 해결책 (변수 선언) - 인수 중 하나에 목록 초기화 사용:
    std::string str2(std::istreambuf_iterator<char>{file}, {});
}

예제

#include <iostream>
#include <memory>
#include <string>
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
int main()
{
    std::string s1("test"); // const char*로부터 생성자
    std::string s2(10, 'a');
    std::unique_ptr<int> p(new int(1));  // OK: explicit 생성자 허용됨
//  std::unique_ptr<int> p = new int(1); // 오류: 생성자가 explicit임
    Foo f(2); // f는 직접 초기화됨:
              // 생성자 매개변수 n은 rvalue 2로부터 복사 초기화됨
              // f.mem은 매개변수 n으로부터 직접 초기화됨
//  Foo f2 = 2; // 오류: 생성자가 explicit임
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

출력:

test aaaaaaaaaa 1 2

참고 항목