중괄호를 사용하여 목록 초기화가 다른 방법보다 나은 이유는 무엇입니까?


405
MyClass a1 {a};     // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);

왜?

SO에 대한 답변을 찾을 수 없으므로 내 질문에 대답하겠습니다.


12
왜 사용하지 auto않습니까?
마크 가르시아

33
그것은 사실이지만 편리하지만 내 의견으로는 가독성을 떨어 뜨 립니다. 코드를 읽을 때 객체의 유형 을 보고 싶습니다 . 객체의 유형이 100 % 확실하다면 왜 자동을 사용합니까? 그리고 목록 초기화 (내 대답 읽기)를 사용하면 항상 올바른지 확인할 수 있습니다.
Oleksiy

102
@Oleksiy : std::map<std::string, std::vector<std::string>>::const_iterator당신과 함께 한마디 부탁드립니다.
Xeo

9
@Oleksiy 이 GotW를 읽는 것이 좋습니다 .
Rapptz

17
@doc 내가 말하고 싶은 것은 using MyContainer = std::map<std::string, std::vector<std::string>>;더 낫다 (특히 템플릿을 작성할 수있는 것처럼!)
JAB

답변:


356

Bjarne Stroustrup의 "The C ++ Programming Language 4th Edition" 에서 기본적으로 복사하여 붙여 넣기 :

목록 초기화 는 축소를 허용하지 않습니다 (§iso.8.5.4). 그건:

  • 정수는 값을 보유 할 수없는 다른 정수로 변환 할 수 없습니다. 예를 들어, char to int는 허용되지만 int to char는 허용되지 않습니다.
  • 부동 소수점 값은 해당 값을 보유 할 수없는 다른 부동 소수점 유형으로 변환 할 수 없습니다. 예를 들어, float to double은 허용되지만 double to float는 허용되지 않습니다.
  • 부동 소수점 값은 정수 유형으로 변환 할 수 없습니다.
  • 정수 값은 부동 소수점 유형으로 변환 할 수 없습니다.

예:

void fun(double val, int val2) {

    int x2 = val; // if val==7.9, x2 becomes 7 (bad)

    char c2 = val2; // if val2==1025, c2 becomes 1 (bad)

    int x3 {val}; // error: possible truncation (good)

    char c3 {val2}; // error: possible narrowing (good)

    char c4 {24}; // OK: 24 can be represented exactly as a char (good)

    char c5 {264}; // error (assuming 8-bit chars): 264 cannot be 
                   // represented as a char (good)

    int x4 {2.0}; // error: no double to int value conversion (good)

}

에만 사용하는 경우 =이보다 선호되는 상황은 {}이다 auto이니셜 라이저에 의해 결정 유형을 얻기 위해 키워드를.

예:

auto z1 {99};   // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99;   // z3 is an int

결론

특별한 이유가없는 한 대체 방법보다 {} 초기화를 선호하십시오.


52
또한 using ()을 함수 선언으로 파싱 할 수 있다는 사실도 있습니다. 말할 수는 T t(x,y,z);없지만 혼동스럽고 일관성이 없습니다 T t(). 그리고 때때로, 당신은 확실히 x말할 수 없습니다 T t(x);.
juanchopanza

84
나는이 답변에 강력히 동의하지 않습니다. ctor가 a를 허용하는 유형이있는 경우 고정 초기화는 완전한 혼란이됩니다 std::initializer_list. RedXIII는이 문제를 언급하고 브러시로 제거하는 반면 완전히 무시합니다. A(5,4)A{5,4}완전히 다른 함수를 호출하고,이 알아야 할 중요한 일이 있습니다. 직관적이지 않은 통화가 발생할 수도 있습니다. {}기본적으로 선호한다고 말하면 사람들이 무슨 일이 일어나고 있는지 오해하게 될 것입니다. 그러나 이것은 당신의 잘못이 아닙니다. 나는 개인적으로 그것이 매우 열악한 생각이라고 생각합니다.
user1520427

13
"user1520427" " 그렇지 않은 강한 이유가없는 한 " 이있는 이유 입니다.
Oleksiy

67
이 질문은 오래되었지만 꽤 적중 했으므로 참조 용으로 여기에 추가하고 있습니다 (페이지의 다른 곳에서는 보지 못했습니다). braced-init-list에서 자동 공제에 대한 새로운 규칙을 사용하는 C ++ 14 에서 이제는 글을 쓸 auto var{ 5 }수 있으며 int더 이상으로 추론 됩니다 std::initializer_list<int>.
Edoardo Sparkon Dominici

12
하하, 모든 의견에서 아직도 무엇을 해야할지 명확하지 않습니다. 분명한 것은 C ++ 사양이 엉망이라는 것입니다!
DrumM

113

목록 초기화를 사용하는 이점에 대해서는 이미 큰 대답이 있지만 개인적으로 경험할 때마다 중괄호를 사용하지 말고 개념적 의미에 의존하게하십시오.

  • 내가 만들고있는 객체가 생성자에 전달하는 값 (예 : 컨테이너, POD 구조체, 원자, 스마트 포인터 등)을 개념적으로 보유하면 중괄호를 사용합니다.
  • 생성자가 일반 함수 호출과 비슷하면 (인수로 매개 변수화되는 다소 복잡한 작업을 수행합니다) 정상적인 함수 호출 구문을 사용하고 있습니다.
  • 기본 초기화에는 항상 중괄호를 사용합니다.
    하나, 그 방법으로 나는 항상 예를 ​​들어 어쨌든 내장 / POD 유형이라고 불리는 기본 생성자가있는 "실제"클래스인지 여부에 관계없이 객체가 초기화 될 것이라고 확신합니다. 둘째, 대부분의 경우 기본 초기화 객체는 종종 "빈"객체를 나타내므로 첫 번째 규칙과 일치합니다.

필자의 경험에 따르면이 규칙 세트는 기본적으로 중괄호를 사용하는 것보다 훨씬 일관되게 적용될 수 있지만 괄호가있는 "일반"함수 호출 구문과 다른 의미를 사용할 수 없거나 다른 의미가있는 경우 모든 예외를 명시 적으로 기억해야합니다. (다른 과부하를 호출).

예를 들어 std::vector다음 과 같은 표준 라이브러리 유형에 잘 맞습니다 .

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments

vector<int> b(10,20);   //Parentheses -> uses arguments to parametrize some functionality,                          
vector<int> c(it1,it2); //like filling the vector with 10 integers or copying a range.

vector<int> d{};      //empty braces -> default constructs vector, which is equivalent
                      //to a vector that is filled with zero elements

11
대부분의 답변에 전적으로 동의하십시오. 그러나 벡터에 빈 괄호를 넣는 것이 중복이라고 생각하지 않습니까? 제네릭 타입 T의 객체를 값으로 초기화해야 할 때 괜찮습니다.하지만 제네릭이 아닌 코드에서 수행하는 목적은 무엇입니까?
Mikhail

8
@ Mikhail : 그것은 확실히 중복이지만 항상 로컬 변수 초기화를 명시 적으로 만드는 습관입니다. 내가 쓴 것처럼, 이것은 주로 편의성에 관한 것이므로 중요한 경우 잊어 버리지 않습니다. 코드 리뷰에서 언급하거나 스타일 가이드에 언급 한 것은 아닙니다.
MikeMB

4
꽤 깨끗한 규칙 세트.
laike9m

5
이것이 가장 좋은 대답입니다. {}는 상속과 같습니다. 남용하기 쉬우므로 코드를 이해하기 어렵습니다.
UKMonkey

2
@MikeMB 예제 : const int &b{}<-초기화되지 않은 참조를 만들지 않고 임시 정수 객체에 바인딩합니다. 두 번째 예 : struct A { const int &b; A():b{} {} };<-는 초기화되지 않은 참조를 만들지 않고 ()임시 정수 객체에 바인딩 한 다음 매달린 상태로 둡니다. GCC -Wall는 두 번째 예를 경고하지 않습니다.
Johannes Schaub-litb

91

이 사용 중괄호 초기화에 대한 많은 이유가 있습니다,하지만 당신은 알고 있어야합니다 생성자가 다른 생성자 선호된다 예외가 기본적으로 생성자 것. 이로 인해 형식 생성자가 초기화 목록이거나 일반 오래된 ctor가 될 수있는 생성자와 템플릿에 문제가 발생 합니다.initializer_list<>T

struct Foo {
    Foo() {}

    Foo(std::initializer_list<Foo>) {
        std::cout << "initializer list" << std::endl;
    }

    Foo(const Foo&) {
        std::cout << "copy ctor" << std::endl;
    }
};

int main() {
    Foo a;
    Foo b(a); // copy ctor
    Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}

이러한 클래스가 발생하지 않는다고 가정하면 초기화 프로그램 목록을 사용하지 않는 이유는 거의 없습니다.


20
이것은 일반적인 프로그래밍에서 매우 중요한 포인트입니다. 템플릿을 작성할 때 시맨틱 (잘 정의되어 있고 객체를 기본 구성하는 경우)이 필요하지 않으면 braced-init-lists (의 표준 이름)를 사용 하지 마십시오 . { ... }initializer_list
Xeo

82
나는 std::initializer_list규칙이 존재 하는 이유를 솔직히 이해하지 못합니다 -그것은 언어에 혼란과 혼란을 더합니다. 생성자 Foo{{a}}를 원한다면 무엇이 잘못 되었나요 std::initializer_list? std::initializer_list다른 모든 과부하보다 우선 하는 것보다 이해하기가 훨씬 쉽습니다 .
user1520427

5
위의 의견에 +1합니다. 왜냐하면 정말 엉망입니다! 논리가 아닙니다. intializer 목록 우선 순위로 바뀌는 Foo{{a}}것보다 훨씬 더 많은 논리를 따르 Foo{a}십시오 (사용자가 hm이라고 생각할 수도 있습니다 ...)
Gabriel

32
기본적으로 C ++ 11은 하나의 엉망을 다른 엉망으로 대체합니다. 죄송합니다. 교체하지 않습니다. 추가됩니다. 그러한 수업을 듣지 않으면 어떻게 알 수 있습니까? 무엇을 당신이 시작하면 없이 std::initializer_list<Foo> 생성자,하지만 될 것입니다 추가 받는 사람Foo 의 인터페이스를 확장하기 위해 어떤 점에서 클래스? 그런 다음 Foo수업의 사용자 가 망쳤습니다.
doc

10
"중괄호 초기화를 사용해야하는 많은 이유"는 무엇입니까? 이 답변은 한 가지 이유 ( initializer_list<>)를 지적합니다.이 이유 는 누가 자신이 선호한다고 말 했는지 자격이 없으며 , 다음과 같은 좋은 사례를 언급합니다. 하지 선호합니다. ~ 30 명의 다른 사람 (2016-04-21 기준)이 도움이된다는 것을 놓친 점은 무엇입니까?
dwanderson

0

Google이 Chromium에서 말하는 것처럼 좁지 않은 빌드로 빌드하지 않는 한 더 안전합니다. 그렇게하면 덜 안전합니다. 이 플래그가 없으면 안전하지 않은 경우는 C ++ 20으로 해결됩니다.

참고 : A) 괄호는 좁아지지 않기 때문에 더 안전합니다. B) 꼬임 브레이커는 개인 생성자 또는 삭제 된 생성자를 무시하고 명시 적 표시 생성자를 암시 적으로 호출 할 수 있기 때문에 안전하지 않습니다.

이 두 결합은 내부의 것이 기본 상수이면 더 안전하지만 객체 인 경우 안전하지 않다는 것을 의미합니다 (C ++ 20에서 수정되었지만)


제공된 샘플 코드를 사용하여 "명시 적"또는 "비공개"생성자를 우회하기 위해 goldbolt.org를 돌아 다니면서 시도해 보았으며 적절한 컴파일러 오류가 발생했습니다. 샘플 코드로 백업 하시겠습니까?
Mark Storer

이것은 C ++ (20)에 대한 제안이 문제에 대한 수정이다 open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1008r1.pdf
앨런 젠슨

1
당신이 말하는 C ++의 어떤 버전을 보여주기 위해 답을 편집한다면, 투표를 바꾸게되어 기쁩니다.
Mark Storer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.