Effective Modern C++ · 5/6
항목 5: 명시적 타입 선언보다는 auto를 선호하라
· Hawk
개요
“auto를 쓰면 타입을 모르잖아요!” 맞아요. 하지만 그게 장점입니다. auto는 더 짧고, 더 안전하고, 더 유연한 코드를 만들어줍니다.
auto의 장점들
1. 초기화 강제 = 더 안전한 코드
int x1; // 초기화 안 됨! 쓰레기값auto x2; // 에러! auto는 초기화 필수auto x3 = 0; // OK2. 긴 타입명에서 해방
Before (auto 없이):
std::unordered_map<std::string, std::vector<Customer>>::iterator iter = customerMap.begin();
// 눈 아프죠?After (auto 사용):
auto iter = customerMap.begin();
// 깔끔!3. 타입 불일치 문제 해결
숨어있는 버그:
std::vector<int> v;unsigned sz = v.size(); // 문제! size()는 std::size_t 반환 // 32비트: unsigned = size_t (OK) // 64비트: unsigned ≠ size_t (위험!)auto로 해결:
auto sz = v.size(); // 항상 올바른 타입!4. 숨은 형변환 방지
성능 문제 예제:
std::unordered_map<std::string, int> m;
// 나쁜 예 - 숨은 복사 발생!for (const std::pair<std::string, int>& p : m) { // 실제 타입은 std::pair<const std::string, int> // const가 빠져서 임시 객체 생성됨!}
// 좋은 예 - auto 사용for (const auto& p : m) { // 정확한 타입으로 추론, 복사 없음}5. 람다 저장이 가능
// 람다는 타입을 쓸 수 없음auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2;};
// std::function은 가능하지만...std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&)> derefUPLess2 = [](const auto& p1, const auto& p2) { return *p1 < *p2; };// 더 느리고, 메모리도 더 씀!성능 차이:
auto람다: 인라인 가능, 크기 = 람다 크기std::function: 간접 호출, 힙 할당 가능, 더 큰 메모리
6. 리팩토링이 쉬워짐
타입 변경 시나리오:
// 원래 코드int makeValue();int val = makeValue();
// 나중에 long으로 변경long makeValue();int val = makeValue(); // 타입 불일치! 수정 필요
// auto를 썼다면?auto val = makeValue(); // 자동으로 long이 됨!실전 예제
반복자와 auto
// 복잡한 타입template<typename It>void dwim(It b, It e) { while (b != e) { // 현재 요소 타입을 직접 쓰기 어려움 typename std::iterator_traits<It>::value_type currValue = *b; // 복잡함
// auto로 간단히! auto currValue = *b; // 간단함 }}컨테이너와 auto
// 인덱스 타입 실수 방지std::vector<int> v = {1, 2, 3};
// 위험: int는 음수 가능, size()는 unsignedfor (int i = 0; i < v.size(); ++i) { }
// 안전: 정확한 타입for (auto i = 0u; i < v.size(); ++i) { }
// 더 나은 방법: range-forfor (auto& elem : v) { }auto를 쓰면 안 되는 경우?
1. 프록시 타입 문제 (항목 6에서 자세히)
std::vector<bool> features(const Widget& w);
auto highPriority = features(w)[5]; // bool이 아님!// std::vector<bool>::reference 타입 (프록시)
bool highPriority = features(w)[5]; // 명시적 변환2. 가독성이 중요할 때
auto result = calculate(); // calculate()가 뭘 반환하지?
// 때로는 명시적이 나음Money result = calculate(); // 아, 돈을 반환하는구나auto 사용 가이드라인
auto를 쓸 때
- 긴 타입명 (특히 템플릿)
- 람다 표현식
- 반복자
- 타입을 정확히 모를 때
- 리팩토링이 잦은 코드
auto를 피할 때
- 프록시 타입
- 타입이 중요한 비즈니스 로직
- 명시적 형변환이 필요할 때
성능 비교
// std::function vs auto 람다auto lambda1 = [](int x) { return x * 2; };std::function<int(int)> lambda2 = [](int x) { return x * 2; };
// 크기 차이sizeof(lambda1); // 1 (캡처 없는 람다)sizeof(lambda2); // 32~64 바이트 (구현에 따라)
// 호출 속도lambda1(5); // 인라인 가능lambda2(5); // 간접 호출핵심 정리
auto의 장점:
- 초기화 강제 → 더 안전
- 타입 추론 → 실수 방지
- 짧은 코드 → 가독성 향상
- 정확한 타입 → 이식성 향상
- 리팩토링 용이 → 유지보수 향상
기억하세요:
// 이전 방식std::unordered_map<std::string, int>::const_iterator iter = m.cbegin();
// 현대적 방식auto iter = m.cbegin();
// 둘 다 같은 일을 하지만, 어느 게 읽기 쉬운가요?결론: auto는 단순히 타이핑을 줄이는 게 아니라, 더 정확하고 유지보수하기 쉬운 코드를 만듭니다!