auto 키워드와 타입 추론
auto 키워드의 소개
C++ 11에서 도입된 auto
키워드는 변수의 타입을 컴파일러가 자동으로 추론하도록 해줍니다.
이는 코드의 가독성을 높이고, 리팩토링을 용이하게 만들며, 템플릿 프로그래밍을 더욱 쉽게 만들어줍니다.
기본 사용법
auto i = 42; // int로 추론
auto f = 3.14; // double로 추론
auto s = "Hello"; // const char*로 추론
auto v = {1, 2, 3}; // std::initializer_list<int>로 추론
auto의 타입 추론 규칙
auto
의 타입 추론은 템플릿 타입 추론과 유사하지만, 완전히 동일하지는 않습니다.
주요 규칙은 다음과 같습니다.
- 초기화 표현식이 참조인 경우, 참조성은 무시됩니다.
const
와volatile
한정자는 보통 무시됩니다. (참조나 포인터가 아닌 경우)- 중괄호 초기화
{}
는std::initializer_list
로 추론됩니다.
int x = 27;
const int cx = x;
const int& rx = x;
auto a = x; // int
auto b = cx; // int (const 무시)
auto c = rx; // int (const와 참조성 무시)
auto& d = x; // int&
auto& e = cx; // const int&
auto& f = rx; // const int&
auto와 함께 사용되는 수식어
auto
는 다양한 수식어와 함께 사용될 수 있습니다.
const auto ca = x; // const int
auto& ra = x; // int&
auto* pa = &x; // int*
const auto& cra = x; // const int&
함수 반환 타입으로서의 auto
C++14부터는 함수의 반환 타입으로 auto
를 사용할 수 있습니다.
auto add(int a, int b) {
return a + b; // 반환 타입은 int로 추론
}
auto get_vector() {
return std::vector<int>{1, 2, 3}; // std::vector<int> 반환
}
이는 특히 복잡한 반환 타입을 가진 함수나 템플릿 함수에서 유용합니다.
decltype과 auto
decltype
은 표현식의 타입을 추론합니다.
auto
와 decltype
을 함께 사용하면 더욱 강력한 타입 추론이 가능합니다.
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}
이를 "후행 반환 타입 구문"이라고 합니다.
C++14부터는 대부분의 경우 다음과 같이 간단히 쓸 수 있습니다.
template<typename T, typename U>
auto add(T t, U u) {
return t + u;
}
auto와 람다 표현식
auto
는 다음 절에서 다룰 람다 표현식의 타입을 캡처하는 데 매우 유용합니다.
auto lambda = [](int x, int y) { return x + y; };
C++ 14부터는 람다 매개변수에도 auto
를 사용할 수 있어 제네릭 람다를 만들 수 있습니다.
auto generic_lambda = [](auto x, auto y) { return x + y; };
auto의 장단점
장점
- 코드의 간결성과 가독성 향상
- 타입 불일치 오류 감소
- 리팩토링 용이성
- 긴 타입 이름 작성 불필요
단점
- 코드만 봐서는 정확한 타입을 알기 어려울 수 있음
- 의도치 않은 타입 추론 가능성
- 초보자에게는 이해하기 어려울 수 있음
auto 사용 시 주의사항
- 초기화 리스트와 함께 사용 시 주의
auto x1 = {1, 2, 3}; // std::initializer_list<int>
auto x2{1}; // std::initializer_list<int>, C++17 이전
auto x3 = {1}; // std::initializer_list<int>
- 프록시 클래스와의 상호작용
std::vector<bool> v = {true, false, true};
auto x = v[0]; // std::vector<bool>::reference, 실제 bool이 아님
- 가독성 저하 방지
때로는 명시적 타입이 코드의 의도를 더 잘 전달할 수 있습니다.
auto
의 과도한 사용은 오히려 가독성을 해칠 수 있습니다.
auto와 코딩 스타일
auto
의 사용에 대한 일반적인 가이드라인
- 명확한 경우에만
auto
사용 - 복잡한 타입이나 템플릿 타입에 주로 사용
- 반복자나 람다 표현식에는 거의 항상
auto
사용 - 코드의 의도를 불분명하게 만들지 않도록 주의
실습 : auto를 활용한 코드 개선
다음은 auto
를 사용하여 코드를 개선하는 예제입니다.
// 개선 전
std::map<std::string, std::vector<int>> data;
for (std::map<std::string, std::vector<int>>::iterator it = data.begin();
it != data.end(); ++it) {
for (std::vector<int>::const_iterator vit = it->second.begin();
vit != it->second.end(); ++vit) {
// 처리 로직
}
}
// 개선 후
std::map<std::string, std::vector<int>> data;
for (const auto& [key, value] : data) { // C++17 구조화된 바인딩 사용
for (const auto& num : value) {
// 처리 로직
}
}
연습 문제
- 다음 코드에서
auto
로 선언된 변수들의 실제 타입을 추론해보세요.
int x = 10;
const int* p = &x;
int& r = x;
auto a = x;
auto b = p;
auto c = *p;
auto& d = r;
auto e = {1, 2, 3};
- 다음 함수 템플릿을
auto
와decltype
을 사용하여 개선해보세요.
template<typename T, typename U>
? multiply(T t, U u) {
return t * u;
}
참고자료
- "Effective Modern C++" by Scott Meyers (특히 Item 5 : Prefer auto to explicit type declarations)
- C++ Core Guidelines : ES.11 : Use auto to avoid redundant repetition of type names
- cppreference.com : auto specifier