타입 추론(auto 포함) (Type Deduction (including auto))¶
프로젝트에 익숙하지 않은 독자에게 코드를 더 명확하게 만들거나 코드를 더 안전하게 만드는 경우에만 유형 추론을 사용하십시오. 명시적인 유형을 작성하는 데 따른 불편함을 피하기 위해서만 사용하지 마세요.
C++에서는 코드에 명시적으로 설명하지 않고 컴파일러가 유형을 추론하도록 허용(또는 요구)하는 여러 가지 컨텍스트가 있습니다.
(이 요약에는 많은 세부 사항과 주의 사항이 생략되어 있습니다. 자세한 내용은 링크를 참조하세요.)
- C++ 유형 이름은 길고 번거로울 수 있으며, 특히 템플릿이나 네임스페이스와 관련된 경우 더욱 그렇습니다.
- 단일 선언이나 작은 코드 영역 내에서 C++ 유형 이름이 반복되는 경우 반복이 가독성에 도움이 되지 않을 수 있습니다.
- 때로는 유형을 추론하도록 두는 것이 더 안전할 때도 있습니다. 이렇게 하면 의도하지 않은 복사나 유형 변환 가능성을 피할 수 있기 때문입니다.
C++ 코드는 일반적으로 유형이 명시적일 때 더 명확하며, 특히 유형 추론이 코드의 먼 부분에서 얻은 정보에 의존하는 경우 더욱 그렇습니다. 다음과 같은 표현에서:
y의 유형이 잘 알려져 있지 않거나 y가 여러 줄 이전에 선언된 경우 결과 유형이 무엇인지 명확하지 않을 수 있습니다.
프로그래머는 유형 추론이 참조 유형을 생성하거나 생성하지 않을 때를 이해해야 합니다. 그렇지 않으면 의도하지 않은 복사본을 얻게 됩니다.
추론된 유형이 인터페이스의 일부로 사용되는 경우 프로그래머는 해당 값만 변경하려는 의도로 해당 유형을 변경할 수 있으며 이로 인해 의도한 것보다 더 급진적인 API 변경이 발생할 수 있습니다.
기본 규칙은 코드를 더 명확하고 안전하게 만들기 위해서만 유형 추론을 사용하고, 단지 명시적인 유형을 작성하는 불편함을 피하기 위해 사용하지 않는다는 것입니다. 코드가 더 명확한지 판단할 때 독자가 반드시 팀에 속하거나 프로젝트에 익숙할 필요는 없으므로 귀하와 리뷰어가 불필요하게 복잡하다고 경험하는 유형은 다른 사람에게 유용한 정보를 제공하는 경우가 많다는 점을 명심하세요. 예를 들어, make_unique
이러한 원칙은 모든 형태의 유형 추론에 적용되지만 세부 사항은 다음 섹션에서 설명하는 것처럼 다양합니다.
함수 템플릿 인수 추론은 거의 항상 괜찮습니다. 유형 추론은 함수 템플릿이 일반 함수 오버로드의 무한 세트처럼 작동할 수 있도록 허용하기 때문에 함수 템플릿과 상호 작용할 것으로 예상되는 기본 방법입니다. 결과적으로 함수 템플릿은 거의 항상 템플릿 인수 추론이 명확하고 안전하거나 컴파일되지 않도록 설계됩니다.
지역 변수의 경우, 명확하거나 관련성이 없는 유형 정보를 제거하여 코드를 더 명확하게 만들기 위해 유형 추론을 사용할 수 있으므로 독자는 코드의 의미 있는 부분에 집중할 수 있습니다.
std::unique_ptr<WidgetWithBellsAndWhistles> widget =
std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2);
absl::flat_hash_map<std::string,
std::unique_ptr<WidgetWithBellsAndWhistles>>::const_iterator
it = my_map_.find(key);
std::array<int, 6> numbers = {4, 8, 15, 16, 23, 42};
auto widget = std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2);
auto it = my_map_.find(key);
std::array numbers = {4, 8, 15, 16, 23, 42};
유형에는 위의 예와 같이 유용한 정보와 상용구가 혼합되어 있는 경우가 있습니다. 유형이 반복자임이 분명하며 많은 상황에서 컨테이너 유형과 심지어 키 유형도 관련이 없지만 값 유형은 아마도 유용할 것입니다. 이러한 상황에서는 관련 정보를 전달하는 명시적 유형으로 지역 변수를 정의하는 것이 가능한 경우가 많습니다.
if (auto it = my_map_.find(key); it != my_map_.end()) {
WidgetWithBellsAndWhistles& widget = *it->second;
// Do stuff with `widget`
}
유형이 템플릿 인스턴스이고 매개변수가 상용구이지만 템플릿 자체가 정보를 제공하는 경우 클래스 템플릿 인수 추론을 사용하여 상용구를 억제할 수 있습니다. 그러나 이것이 실제로 의미 있는 이점을 제공하는 경우는 매우 드뭅니다. 클래스 템플릿 인수 추론에도 별도의 스타일 규칙이 적용됩니다.
map과 유사한 컨테이너 요소의 명시적 유형은 잘못되기 쉽습니다. 이러한 경우 키와 값에 대한 구조적 바인딩은 쌍을 나타내는 단일 변수보다 거의 항상 더 명확합니다.
더 간단한 옵션이 작동한다면 decltype(auto)를 사용하지 마세요. 상당히 모호한 기능이기 때문에 코드 명확성에 비용이 많이 듭니다.
변수 선언에 auto를 사용하는 경우에 대한 자세한 내용은 TotW #232를 참조하세요.
함수 본문에 매우 적은 수의 반환 문이 있고 다른 코드가 거의 없는 경우에만 반환 유형 추론(함수 및 람다 모두에 대해)을 사용하십시오. 그렇지 않으면 독자가 반환 유형이 무엇인지 한눈에 알 수 없기 때문입니다. 또한 추론된 반환 유형이 있는 함수는 추상화 경계를 정의하지 않기 때문에 함수 또는 람다의 범위가 매우 좁은 경우에만 이를 사용하십시오. 구현은 인터페이스입니다. 특히 헤더 파일의 공개 함수는 반환 유형을 추론한 적이 거의 없어야 합니다.
대신 매개변수에 특정 유형을 제공할 수 있는 경우 매개변수 유형 추론(자동 또는 템플릿 매개변수 사용)을 사용하지 마세요. 하나의 유형으로만 호출되는 경우, 정의된 위치에 매우 가깝게 호출되지 않는 한(독자가 둘 다 쉽게 볼 수 있도록) 해당 유형을 명시적으로 만드는 것이 거의 항상 더 명확합니다. 또는 인터페이스에 전달되는 람다 너무 잘 알려져서 결국 어떤 유형으로 호출될지 분명합니다(예: 위의 std::sort 예제).
람다가 아닌 함수에서는 자동 매개변수를 사용하지 마세요. 대신 명명된 템플릿 매개변수를 사용하세요. 왜냐하면 함수가 템플릿이라는 사실을 더 명시적으로 나타내기 때문입니다.
람다 식에서 매개변수 유형을 참조해야 하는 경우 해당 유형을 템플릿 매개변수로 만드는 것이 좋습니다.
Init 캡처는 유형 추론에 대한 일반 규칙을 대체하는 보다 구체적인 스타일 규칙에 의해 다루어집니다.
다른 형태의 유형 추론과 달리 구조적 바인딩은 더 큰 개체의 요소에 의미 있는 이름을 제공하여 독자에게 실제로 추가 정보를 제공할 수 있습니다. 이는 auto가 그렇지 않은 경우에도 구조화된 바인딩 선언이 명시적 유형에 비해 순 가독성 향상을 제공할 수 있음을 의미합니다. 구조화된 바인딩은 개체가 쌍이나 튜플인 경우(위의 삽입 예에서처럼) 특히 유용합니다. 왜냐하면 처음부터 의미 있는 필드 이름이 없기 때문입니다. 그러나 insert와 같은 기존 API에서 강제로 사용하지 않는 한 일반적으로 쌍이나 튜플을 사용해서는 안 됩니다.
std::map
바인딩되는 개체가 구조체인 경우 사용법에 더 구체적인 이름을 제공하는 것이 도움이 될 수 있지만 이는 이름이 필드 이름보다 독자가 쉽게 인식할 수 없다는 의미일 수도 있다는 점을 명심하세요. 함수 매개변수 주석과 동일한 구문을 사용하여 바인딩 이름과 일치하지 않는 경우 기본 필드의 이름을 나타내는 주석을 사용하는 것이 좋습니다.
함수 매개변수 주석과 마찬가지로 이를 통해 필드 순서가 잘못되었는지 감지할 수 있는 도구를 사용할 수 있습니다.
이해하기 쉽게 설명하기¶
핵심: 더 명확하거나 더 안전할 때만 타입 추론¶
auto 같은 타입 추론은 코드를 더 명확하거나 더 안전하게 만들 때만 쓰세요. 단지 긴 타입을 적기 귀찮아서 쓰면 안 됩니다. 명확한지 판단할 때의 독자는 "팀 밖의, 프로젝트에 익숙하지 않은 사람"임을 기억하세요.
// 좋음: 타입이 상용구라 auto가 더 깔끔
auto widget = std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2);
auto it = my_map_.find(key);
// 주의: 결과 타입이 한눈에 안 보이면 명시가 낫다
auto i = y.Find(key); // y가 멀리 선언됐다면 불명확
형태별 지침¶
- 함수 템플릿 인자 추론: 거의 항상 OK.
- 지역 변수
auto: 상용구 타입을 지우고 의미 있는 부분에 집중시킬 때 좋음. 단 참조/복사 여부에 주의. - 반환 타입 추론: 함수 본문이 매우 짧고 범위가 좁을 때만. 헤더의 공개 함수는 거의 추론하지 마세요(구현이 인터페이스가 됨).
- 매개변수 추론: 람다가 아닌 함수에서
auto매개변수 대신 명명된 템플릿 매개변수를 쓰세요(템플릿임이 드러나도록). decltype(auto): 더 단순한 방법이 되면 쓰지 마세요.
구조적 바인딩은 예외적으로 권장¶
구조적 바인딩(structured binding)은 요소에 의미 있는 이름을 주므로 auto와 달리 정보를 더합니다. 특히 std::map의 원소나 pair/tuple에서 유용하며, 키 타입의 const 누락 같은 버그도 막아줍니다.