코딩 스타일은 궁극적으로 주관적이며 상당한 성능상의 이점이 없을 것입니다. 그러나 여기에 균일 한 초기화를 자유로이 사용하면 얻을 수 있다고 말할 것입니다.
중복 유형 이름 최소화
다음을 고려하세요:
vec3 GetValue()
{
return vec3(x, y, z);
}
왜 vec3
두 번 입력해야 합니까? 그 점이 있습니까? 컴파일러는 함수가 무엇을 반환하는지 잘 알고 있습니다. 왜 "이 값으로 반환하는 것을 생성자에게 호출하여 반환합니까?" 균일 한 초기화를 통해 다음을 수행 할 수 있습니다.
vec3 GetValue()
{
return {x, y, z};
}
모든 것이 작동합니다.
함수 인수가 더 좋습니다. 이걸 고려하세요:
void DoSomething(const std::string &str);
DoSomething("A string.");
암시 적으로 std::string
자체를 빌드하는 방법을 알고 있기 때문에 유형 이름을 입력하지 않아도 작동합니다 const char*
. 훌륭합니다. 그러나 RapidXML에 따르면 해당 문자열이 나온다면 어떨까요? 또는 루아 문자열. 즉, 실제로 문자열의 길이를 미리 알고 있다고 가정 해 봅시다. 를 std::string
생성하는 생성자는 const char*
그냥 a를 전달하면 문자열의 길이를 가져와야합니다 const char*
.
그래도 명시 적으로 길이가 걸리는 과부하가 있습니다. 그러나 그것을 사용하려면 다음을 수행해야합니다 DoSomething(std::string(strValue, strLen))
. 왜 여분의 typename이 있습니까? 컴파일러는 유형이 무엇인지 알고 있습니다. 와 마찬가지로 auto
추가 유형 이름을 피할 수 있습니다.
DoSomething({strValue, strLen});
그냥 작동합니다. 타입 이름도없고, 소란도없고, 아무것도 없습니다. 컴파일러는 작업을 수행하고 코드는 더 짧으며 모두가 행복합니다.
물론 첫 번째 버전 ( DoSomething(std::string(strValue, strLen))
)이 더 읽기 쉽다 는 주장이 있습니다 . 즉, 무슨 일이 일어나고 있고 누가 무엇을하고 있는지가 분명합니다. 어느 정도는 사실이다. 균일 한 초기화 기반 코드를 이해하려면 함수 프로토 타입을 살펴 봐야합니다. 이것이 어떤 사람들이 비 const 참조로 매개 변수를 전달해서는 안되는 것과 같은 이유입니다. 따라서 값이 수정되는 경우 콜 사이트에서 볼 수 있습니다.
그러나 똑같이 말할 수있다 auto
. 당신이 얻는 것을 알기 auto v = GetSomething();
위해서는의 정의를 살펴 봐야합니다 GetSomething
. 그러나 auto
일단 당신이 그것에 접근하면 거의 무모한 포기와 함께 사용되는 것을 멈추지 않았습니다 . 개인적으로 일단 익숙해지면 괜찮을 것 같습니다. 특히 좋은 IDE.
가장 Vexing 구문 분석을하지 마십시오
코드는 다음과 같습니다.
class Bar;
void Func()
{
int foo(Bar());
}
팝 퀴즈 : 무엇 foo
입니까? "변수"라고 대답하면 틀린 것입니다. 실제로 매개 변수로 사용을 반환하는 함수를 취하는 함수의 프로토 타입입니다 Bar
, 그리고 foo
함수의 반환 값은 int 형이다.
이것을 C ++의 "가장 Vexing 구문 분석"이라고합니다. 인간에게는 전혀 의미가 없기 때문입니다. 그러나 C ++의 규칙은 슬프게도 이것을 요구합니다. 함수 프로토 타입으로 해석 될 수 있다면 그렇게 될 것 입니다. 문제는 Bar()
; 그것은 두 가지 중 하나 일 수 있습니다. 이라는 유형이 될 수 있으며 Bar
이는 임시를 작성 중임을 의미합니다. 또는 매개 변수를 사용하지 않고 a를 반환하는 함수일 수 있습니다 Bar
.
균일 한 초기화는 함수 프로토 타입으로 해석 될 수 없습니다.
class Bar;
void Func()
{
int foo{Bar{}};
}
Bar{}
항상 임시를 만듭니다. int foo{...}
항상 변수를 만듭니다.
사용하려는 경우가 많지만 Typename()
C ++의 구문 분석 규칙으로 인해 불가능한 경우가 있습니다 . 로 Typename{}
, 모호함이 없습니다.
하지 말아야 할 이유
포기한 유일한 힘은 좁아지는 것입니다. 균일 한 초기화로 큰 값으로 작은 값을 초기화 할 수 없습니다.
int val{5.2};
컴파일되지 않습니다. 구식 초기화로 할 수 있지만 균일 한 초기화는 불가능합니다.
이니셜 라이저 목록을 실제로 작동시키기 위해 부분적으로 수행되었습니다. 그렇지 않으면 이니셜 라이저 목록의 유형과 관련하여 많은 모호한 경우가 있습니다.
물론, 어떤 사람들은 그러한 코드 가 컴파일 할 자격 이 없다고 주장 할 수도 있습니다 . 나는 개인적으로 동의합니다. 좁히는 것은 매우 위험하며 불쾌한 행동을 유발할 수 있습니다. 컴파일러 단계에서 이러한 문제를 조기에 파악하는 것이 가장 좋습니다. 최소한 좁히는 것은 누군가 코드에 대해 너무 열심히 생각하지 않는다는 것을 암시합니다.
경고 수준이 높으면 컴파일러에서 일반적으로 이러한 종류의 경고를 표시합니다. 실제로이 모든 것은 경고를 강제 오류로 만드는 것입니다. 어떤 사람들은 당신이 어쨌든 그렇게해야한다고 말할 수도 있습니다.)
하지 말아야 할 또 다른 이유가 있습니다.
std::vector<int> v{100};
이것은 무엇을 하는가? vector<int>
기본 구성 항목 100 개로을 만들 수 있습니다 . 또는 vector<int>
값이 인 1 개의 항목으로를 만들 수 있습니다 100
. 둘 다 이론적으로 가능합니다.
실제로는 후자를 수행합니다.
왜? 이니셜 라이저 목록은 균일 한 초기화와 동일한 구문을 사용합니다. 모호한 경우 어떻게해야하는지 설명하는 규칙이 필요합니다. 규칙은 매우 간단합니다. 컴파일러 가 중괄호 초기화 목록과 함께 초기화 목록 생성자를 사용할 수 있으면 됩니다 . 이후 vector<int>
취하는 초기화 목록 생성자가 initializer_list<int>
, 그리고 {100} 유효한 수를 initializer_list<int>
, 따라서 해야합니다 .
사이징 생성자를 얻으려면 ()
대신을 사용해야 합니다 {}
.
이것이 vector
정수로 변환 할 수없는 것이면 이런 일이 발생하지 않습니다. initializer_list는 해당 vector
유형 의 초기화 목록 생성자에 맞지 않으므로 컴파일러는 다른 생성자에서 자유롭게 선택할 수 있습니다.