C++ auto 타입 추론 에러 | "cannot deduce" 컴파일 에러 해결
이 글의 핵심
C++ auto 타입 추론 에러에 대한 실전 가이드입니다.
들어가며: “auto를 썼더니 타입이 이상해요"
"auto로 받았는데 참조가 아니라 복사가 되었어요”
C++11의 auto는 타입 추론을 자동화해 코드를 간결하게 만들지만, 참조와 const가 제거되는 등 예상과 다른 타입이 추론될 수 있습니다.
// ❌ 참조 손실
std::vector<int> vec = {1, 2, 3, 4, 5};
auto& ref = vec[0]; // int&
auto val = ref; // int (참조 제거!)
val = 99; // vec[0]은 변경 안 됨
이 글에서 다루는 것:
- auto 타입 추론 규칙
- 참조와 const 손실
- auto vs auto& vs auto&&
- 자주 나오는 auto 에러 8가지
- AAA (Almost Always Auto) 스타일
목차
1. auto 타입 추론 규칙
규칙 1: 참조 제거
int x = 42;
int& ref = x;
auto a = ref; // int (참조 제거)
a = 99; // x는 변경 안 됨
auto& b = ref; // int& (참조 유지)
b = 99; // x가 99로 변경
규칙 2: const 제거
const int x = 42;
auto a = x; // int (const 제거)
a = 99; // OK
const auto b = x; // const int (const 유지)
// b = 99; // 컴파일 에러
규칙 3: 배열 → 포인터
int arr[5] = {1, 2, 3, 4, 5};
auto a = arr; // int* (배열 → 포인터)
auto& b = arr; // int (&)[5] (배열 참조)
2. auto vs auto& vs auto&&
auto: 값 복사
std::vector<int> vec = {1, 2, 3};
auto a = vec[0]; // int (복사)
a = 99; // vec[0]은 변경 안 됨
auto&: 좌측값 참조
std::vector<int> vec = {1, 2, 3};
auto& a = vec[0]; // int& (참조)
a = 99; // vec[0]이 99로 변경
const auto&: const 참조
std::vector<int> vec = {1, 2, 3};
const auto& a = vec[0]; // const int& (const 참조)
// a = 99; // 컴파일 에러
auto&&: 전달 참조 (Universal Reference)
int x = 42;
auto&& a = x; // int& (lvalue → lvalue 참조)
auto&& b = 42; // int&& (rvalue → rvalue 참조)
auto&& c = vec[0]; // int& (lvalue)
사용 시기: 완벽한 전달 (perfect forwarding).
3. 자주 나오는 에러 8가지
에러 1: 초기화 없음
// ❌ 초기화 필요
auto x; // 컴파일 에러
// error: declaration of 'auto x' has no initializer
// ✅ 초기화
auto x = 42;
에러 2: 참조 손실
// ❌ 참조 손실
std::vector<int> vec = {1, 2, 3};
for (auto x : vec) { // int (복사)
x = 99; // 복사본 수정
}
// vec은 여전히 {1, 2, 3}
// ✅ 참조 유지
for (auto& x : vec) { // int&
x = 99;
}
에러 3: const 손실
// ❌ const 손실
const int x = 42;
auto a = x; // int (const 제거)
a = 99; // OK (의도와 다를 수 있음)
// ✅ const 유지
const auto b = x; // const int
// b = 99; // 컴파일 에러
에러 4: 프록시 객체 (vector)
// ❌ 프록시 객체
std::vector<bool> vec = {true, false, true};
auto x = vec[0]; // vector<bool>::reference (프록시)
// x는 bool이 아님!
vec.clear();
bool b = x; // ❌ 댕글링 참조
// ✅ 명시적 타입
bool x = vec[0]; // bool로 변환
에러 5: 초기화 리스트 추론
// ❌ 초기화 리스트
auto x = {1, 2, 3}; // std::initializer_list<int>
// ✅ 명시적 타입
std::vector<int> x = {1, 2, 3};
에러 6: 함수 반환 타입 불일치
// ❌ 여러 return 타입
auto foo(bool flag) {
if (flag) {
return 42; // int
} else {
return 3.14; // double
}
}
// error: inconsistent deduction for auto return type: 'int' and then 'double'
// ✅ 명시적 타입
double foo(bool flag) {
if (flag) {
return 42;
} else {
return 3.14;
}
}
에러 7: 포인터 vs 참조 혼동
// ❌ 포인터로 추론
int x = 42;
auto p = &x; // int* (포인터)
*p = 99; // OK
p = nullptr; // OK (의도와 다를 수 있음)
// ✅ 참조로 명시
auto& r = x; // int&
r = 99; // OK
// r = nullptr; // 컴파일 에러
에러 8: 템플릿 인자 추론 실패
// ❌ 추론 실패
template <typename T>
void foo(T value) {
auto x = value; // OK
}
foo(42); // T = int
// ❌ 함수 포인터
auto func = foo; // 컴파일 에러 (템플릿 함수)
// error: cannot deduce template arguments
// ✅ 명시적 타입
void (*func)(int) = foo<int>;
4. AAA 스타일
AAA (Almost Always Auto)
AAA는 거의 모든 곳에 auto를 사용하는 코딩 스타일입니다.
// ❌ 명시적 타입
std::vector<int> vec = {1, 2, 3};
std::vector<int>::iterator it = vec.begin();
std::map<std::string, int>::const_iterator mit = scores.begin();
// ✅ AAA 스타일
auto vec = std::vector<int>{1, 2, 3};
auto it = vec.begin();
auto mit = scores.cbegin();
장점:
- 코드가 간결
- 타입 변경 시 수정 최소화
- 초기화 강제
단점:
- 타입이 불명확할 수 있음
- 참조/const 손실 주의
정리
auto 사용 규칙
| 상황 | 권장 | 이유 |
|---|---|---|
| 읽기만 | const auto& | 복사 없음 |
| 수정 | auto& | 참조 |
| 복사 의도 | auto | 명확 |
| 완벽한 전달 | auto&& | 전달 참조 |
| 반복자 | auto | 간결 |
| 함수 반환 | 명시적 타입 | 명확성 |
auto 에러 방지 체크리스트
- 초기화를 했는가?
- 참조가 필요하면 auto&를 사용하는가?
- const가 필요하면 const auto를 사용하는가?
- 프록시 객체를 주의하는가? (vector
) - 함수 반환 타입이 일관적인가?
핵심 규칙
- 읽기만: const auto& (복사 없음)
- 수정: auto& (참조)
- 초기화 필수
- 참조와 const 명시 (auto는 제거)
- 프록시 객체 주의 (vector
)
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ auto 기초 | 타입 추론 완벽 가이드
- C++ 타입 추론 | auto·decltype·템플릿
- C++ decltype | 타입 추론 고급
- C++ AAA 스타일 | Almost Always Auto
마치며
auto는 코드를 간결하게 만들지만, 참조와 const가 제거되므로 주의해야 합니다.
핵심 원칙:
- 읽기만: const auto&
- 수정: auto&
- 초기화 필수
- 함수 반환은 명시적 타입
const auto&를 습관화하면 불필요한 복사를 제거하고 성능을 높일 수 있습니다.
다음 단계: auto를 이해했다면, C++ 템플릿 타입 추론에서 더 깊이 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |