중괄호로 묶인 이니셜 라이저는 언제 사용합니까?


94

C ++ 11에는 클래스 초기화를위한 새로운 구문이있어 변수를 초기화하는 방법에 대한 많은 가능성을 제공합니다.

{ // Example 1
  int b(1);
  int a{1};
  int c = 1;
  int d = {1};
}
{ // Example 2
  std::complex<double> b(3,4);
  std::complex<double> a{3,4};
  std::complex<double> c = {3,4};
  auto d = std::complex<double>(3,4);
  auto e = std::complex<double>{3,4};
}
{ // Example 3
  std::string a(3,'x');
  std::string b{3,'x'}; // oops
}
{ // Example 4
  std::function<int(int,int)> a(std::plus<int>());
  std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
  std::unique_ptr<int> a(new int(5));
  std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
  std::locale::global(std::locale("")); // copied from 22.4.8.3
  std::locale::global(std::locale{""});
}
{ // Example 7
  std::default_random_engine a {}; // Stroustrup's FAQ
  std::default_random_engine b;
}
{ // Example 8
  duration<long> a = 5; // Stroustrup's FAQ too
  duration<long> b(5);
  duration<long> c {5};
}

선언하는 각 변수에 대해 어떤 초기화 구문을 사용해야하는지 생각해야하는데 이로 인해 코딩 속도가 느려집니다. 나는 그것이 중괄호를 도입하려는 의도가 아니라고 확신합니다.

템플릿 코드와 관련하여 구문을 변경하면 다른 의미로 이어질 수 있으므로 올바른 방법으로가는 것이 필수적입니다.

어떤 구문을 선택해야하는지 보편적 인 지침이 있는지 궁금합니다.


1
{} 초기화에서 의도하지 않은 동작의 예 : string (50, 'x') vs string {50, 'x'} 여기
P i

답변:


64

나는 생각 다음이 좋은 지침이 될 수 :

  • 초기화하는 (단일) 값이 객체 의 정확한 값 이되도록 의도 된 경우 복사 ( =) 초기화를 사용합니다 (오류가 발생해도 실수로 명시 적 생성자를 호출하지 않으므로 일반적으로 제공된 값을 해석합니다.) 다르게). 복사 초기화를 사용할 수없는 곳에서는 중괄호 초기화에 올바른 의미가 있는지 확인하고, 그렇다면이를 사용합니다. 그렇지 않으면 괄호 초기화를 사용합니다 (사용할 수없는 경우 어쨌든 운이 없습니다).

  • 초기화하는 값이 객체에 저장할 값 목록 (예 : 벡터 / 배열의 요소 또는 복소수의 실수 / 허수 부분) 인 경우 가능한 경우 중괄호 초기화를 사용합니다.

  • 당신이 초기화되는 값이 있다면 하지 저장되는 값 만 설명 개체를 사용 괄호의 의도 된 값 / 상태. 예는의 크기 인수 vector또는의 파일 이름 인수입니다 fstream.


4
@ user1304032 : 로케일은 문자열이 아니므로 복사 초기화를 사용하지 않습니다. 로케일에는 문자열도 포함되지 않으므로 (해당 문자열을 구현 세부 정보로 저장할 수 있지만 목적은 아님) 중괄호 초기화를 사용하지 않습니다. 따라서 지침은 괄호 초기화를 사용하도록 지시합니다.
celtschk

2
개인적으로이 가이드 라인이 마음에 들었고 일반 코드에서도 잘 작동합니다. 몇 가지 예외 ( T {}또는 가장 짜증나는 구문 분석 과 같은 구문상의 이유 )가 있지만 일반적으로 이것이 좋은 조언이라고 생각합니다. 이것은 내 주관적인 의견이므로 다른 답변도 살펴 봐야합니다.
helami

2
@celtschk : 복사 할 수없고 이동할 수없는 유형에는 작동하지 않습니다. type var{};않습니다.
ildjarn 2012

2
@celtschk : 자주 발생하는 것이 아니라 타이핑이 적고 더 많은 컨텍스트에서 작동하므로 단점은 무엇입니까?
ildjarn 2012

2
지침은 확실히 복사 초기화를 요구하지 않습니다. ;-]
ildjarn 2012

26

나는 보편적 인 지침이 결코 없을 것이라고 확신합니다. 내 접근 방식은 항상 중괄호를 사용하여

  1. 이니셜 라이저 목록 생성자가 다른 생성자보다 우선합니다.
  2. 모든 표준 라이브러리 컨테이너와 std :: basic_string에는 이니셜 라이저 목록 생성자가 있습니다.
  3. 중괄호 초기화는 축소 변환을 허용하지 않습니다.

따라서 둥근 중괄호와 중괄호는 서로 바꿔서 사용할 수 없습니다. 그러나 그들이 어디에서 다른지 알면 대부분의 경우에 둥근 대괄호 초기화를 사용할 수 있습니다 (현재 컴파일러 버그가 아닌 경우).


6
중괄호는 실수로 목록 생성자를 호출 할 수 있다는 단점이 있습니다. 둥근 대괄호는 그렇지 않습니다. 기본적으로 둥근 괄호를 사용하는 이유가 아닌가요?
helami

4
@user : int i = 0;나는 아무도 int i{0}거기에서 사용할 것이라고 생각하지 않으며 혼란 스러울 수 있습니다 (또한 0if type int이므로 축소 가 없습니다 ). 그 밖의 모든 경우에는 Juancho의 조언을 따릅니다. {}를 선호하고, 안되는 몇 가지 경우를 조심하세요. 이니셜 라이저 목록을 생성자 인수로 사용하는 유형은 많지 않습니다. 컨테이너 및 컨테이너와 유사한 유형 (튜플 ...)이이를 가질 것으로 예상 할 수 있지만 대부분의 코드는 적절한 생성자를 호출합니다.
David Rodríguez-dribeas

3
@ user1304032 축소에 관심이 있는지 여부에 따라 다릅니다. 그래서 나는 컴파일러 int i{some floating point}가 조용히 자르기보다는 그것이 오류 라고 알려주는 것을 선호합니다 .
juanchopanza 2012

3
"{} 선호,하지 말아야 할 몇 가지 경우에주의하십시오"와 관련하여 : 두 클래스가 의미 상 동등한 생성자를 가지고 있지만 한 클래스에도 이니셜 라이저 목록이 있다고 가정하십시오. 두 개의 동등한 생성자를 다르게 호출해야합니까?
helami

3
@helami : "두 클래스가 의미 적으로 동일한 생성자를 가지고 있지만 한 클래스에도 이니셜 라이저 목록이 있습니다. 두 개의 동등한 생성자를 다르게 호출해야합니까?" 내가 가장 복잡한 구문 분석을 실행한다고 가정 해 보겠습니다. 그에 일어날 수있는 모든 인스턴스의 생성자입니다. {}절대적으로 할 수없는 경우를 제외하고 "초기화"를 의미 하기 위해 사용 하는 경우이를 피하는 것이 훨씬 쉽습니다 .
Nicol Bolas 2012

16

일반 코드 (예 : 템플릿) 외에 어디에서나 중괄호를 사용할 수 있습니다 . 한 가지 장점은 모든 곳에서 작동한다는 것입니다. 예를 들어 클래스 내 초기화에서도 마찬가지입니다.

struct foo {
    // Ok
    std::string a = { "foo" };

    // Also ok
    std::string b { "bar" };

    // Not possible
    std::string c("qux");

    // For completeness this is possible
    std::string d = "baz";
};

또는 함수 인수의 경우 :

void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));

변수의 경우 T t = { init };또는 T t { init };스타일 사이에 많은주의를 기울이지 않습니다. 그 차이가 사소하고 최악의 경우 explicit생성자 오용에 대한 유용한 컴파일러 메시지 만 표시 됩니다.

std::initializer_list분명히 때때로 비 std::initializer_list생성자가 필요 하지만 허용하는 유형의 경우 (고전적인 예는 std::vector<int> twenty_answers(20, 42);)입니다. 그러면 중괄호를 사용하지 않는 것이 좋습니다.


일반 코드 (예 : 템플릿)에 관해서는 맨 마지막 단락에서 경고를 발생시켜야합니다. 다음을 고려하세요:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }

그런 auto p = make_unique<std::vector<T>>(20, T {});경우, 크기 2의 벡터를 생성 T예이다 int경우 또는 크기 (20)의 벡터 T이다 std::string. 여기에서 뭔가 매우 잘못되었다는 매우 확실한 신호는 여기서 당신을 구할 수있는 특성이 없다는 것입니다 (예 : SFINAE) : std::is_constructible직접 초기화의 관점에서, 우리는 직접-초기화를 사용하는 중괄호 초기화를 사용합니다. 방해 를 받는 생성자가없는 경우에만 초기화 std::initializer_list. 마찬가지로 std::is_convertible도움이되지 않습니다.

나는 그것을 고칠 수있는 특성을 실제로 손으로 굴리는 것이 가능한지 조사했지만 그것에 대해 지나치게 낙관적이지는 않습니다. 어쨌든 나는 우리가 많이 놓칠 것이라고 생각하지 않는다. 나는 그 make_unique<T>(foo, bar)결과에 상응하는 구조 가된다는 사실 T(foo, bar)이 매우 직관적 이라고 생각한다 . 특히 감안할 때 make_unique<T>({ foo, bar })매우 비슷하며 경우에만 의미가 있습니다 foobar같은 종류가 있습니다.

따라서 일반 코드의 경우 값 초기화 (예 : T t {};또는 T t = {};) 에만 중괄호를 사용합니다 . 이는 매우 편리하며 C ++ 03 방식보다 우수하다고 생각합니다 T t = T();. 그렇지 않으면 직접 초기화 구문 (예 :) T t(a0, a1, a2);또는 기본 구성 ( T t; stream >> t;내가 생각하는 유일한 경우)입니다.

그렇다고 모든 중괄호가 나쁘다는 의미는 아닙니다 . 이전 예제를 수정하여 고려하십시오.

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }

std::unique_ptr<T>실제 유형이 템플릿 매개 변수에 따라 달라 지 더라도을 구성하는 데 여전히 중괄호를 사용합니다 T.


내 예제의 일부를 @interjay하는 것은 참으로 대신 예를 부호없는 형식을 사용해야 할 수도 있습니다 make_unique<T>(20u, T {})에 대한 T중 하나 인 unsignedstd::string. 세부 사항에 대해 너무 확실하지 않습니다. (예를 들어 완전 전달 함수와 관련하여 직접 초기화 대 중괄호 초기화에 대한 기대에 대해서도 언급했습니다.) std::string c("qux");문법에서 멤버 함수 선언과의 모호성을 피하기 위해 클래스 내 초기화로 작동하도록 지정되지 않았습니다.
Luc Danton 2012

@interjay 나는 첫 번째 요점에 동의하지 않습니다. 8.5.4 목록 초기화 및 13.3.1.7 초기화에 의한 목록 초기화를 확인하십시오. 두 번째에 관해서는 내가 작성한 ( 클래스 내 초기화 에 관한 것 ) 및 / 또는 C ++ 문법 (예 : brace-or-equal-initializer 를 참조하는 member-declarator )을 자세히 살펴 봐야 합니다.
Luc Danton 2012

흠, 맞아요-이전에 GCC 4.5로 테스트했는데 제가 말한 내용을 확인하는 것 같았지만 GCC 4.6이 동의합니다. 그리고 나는 당신이 클래스 내 초기화에 대해 이야기하고 있다는 사실을 놓쳤습니다. 죄송합니다.
interjay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.