컴파일러가 변수의 타입을 자동으로 결정하는 기능
개발자가 명시적으로 타입을 작성하지 않아도 컴파일러가 초기화 값을 보고 타입을 추론한다.
변수 선언 시 타입을 auto로 지정하면 초기화 값으로부터 타입을 추론합니다.
#include <iostream>
#include <vector>
int main()
{
// =================================================================
// C++11: 타입 추론 (auto)
// =================================================================
std::cout << "--- 기본 타입 추론 ---" << std::endl;
// 1. 기본 타입
auto x = 10; // 초기화 값 10을 보고 int로 추론
auto y = 3.14; // 3.14를 보고 double로 추론
auto s = "Hello"; // 문자열 리터럴을 보고 const char* 로 추론
std::cout << "x = 10; -> x는 int 타입" << std::endl;
std::cout << "y = 3.14; -> y는 double 타입" << std::endl;
std::cout << "s = \"Hello\"; -> s는 const char* 타입" << std::endl;
std::vector<int> v = { 1, 2, 3 };
// 2. 반복자 (복잡한 타입 이름 대체)
// auto를 사용하지 않을 경우: std::vector<int>::iterator it1 = v.begin();
// auto를 사용하면 코드가 훨씬 간결해집니다.
auto it1 = v.begin();
std::cout << "\n반복자의 첫 번째 요소: " << *it1 << std::endl;
// =================================================================
// 3. 범위 기반 for문 (Range-based for loop)
// =================================================================
std::cout << "\n--- 범위 기반 for문 ---" << std::endl;
std::cout << "원본 벡터 v: ";
for (int elem : v) { std::cout << elem << " "; }
std::cout << std::endl;
// 'for (auto elem : v)' -> 요소를 값으로 복사 (call-by-value)
std::cout << "\n'for (auto elem : v)' 실행 (값 복사):" << std::endl;
std::cout << " -> 루프 안에서 값을 수정해도 원본 벡터는 바뀌지 않음" << std::endl;
for (auto elem : v)
{
// 여기서 elem은 v의 원소를 복사한 새로운 변수입니다.
std::cout << elem << " ";
}
std::cout << std::endl;
// 'for (auto& elem : v)' -> 요소를 참조로 가져옴 (call-by-reference)
std::cout << "\n'for (auto& elem : v)' 실행 (참조):" << std::endl;
std::cout << " -> 루프 안에서 값을 수정하면 원본 벡터가 바뀜" << std::endl;
for (auto& elem : v)
{
// 여기서 elem은 v의 원소 자체를 가리키는 별명(참조)입니다.
elem *= 2;
}
std::cout << "수정 후 벡터 v: ";
for (auto elem : v)
{
std::cout << elem << " ";
}
std::cout << std::endl;
// =================================================================
// 4. 람다(Lambda) 함수
// =================================================================
std::cout << "\n--- 람다 함수 ---" << std::endl;
// 람다 함수의 실제 타입은 복잡하고 이름이 없으므로 auto로 받는 것이 편리합니다.
auto lambda = [](int val) {
return val * 2;
};
int result = lambda(5); // lambda 함수 호출
std::cout << "lambda(5) 실행 결과: " << result << std::endl;
return 0;
}
#include <iostream> #include <vector> // ================================================================= // 1. 함수의 반환 타입 추론 (Return Type Deduction) // ================================================================= // C++14부터 함수의 반환 타입을 auto로 지정하면, 컴파일러가 return 문을 보고 // 반환 타입을 자동으로 추론합니다. auto add(int a, int b) { // a + b의 결과가 int이므로, 이 함수의 반환 타입은 int로 추론됩니다. return a + b; } // std::vector<int> 와 같이 복잡한 타입도 간단하게 표현할 수 있습니다. auto getVector() { return std::vector<int>{ 1, 2, 3, 4 }; } // 함수 내에 return 문이 여러 개 있더라도, 모두 같은 타입으로 추론된다면 사용 가능합니다. auto getValue(bool flag) { if (flag) { return 20; // int로 추론 } else { return 30; // int로 추론 } // 만약 return 30.0; 처럼 다른 타입이 섞여있으면 컴파일 에러가 발생합니다. } int main() { std::cout << "--- 1. 함수 반환 타입 추론 ---" << std::endl; std::cout << "add(10, 20) 결과: " << add(10, 20) << std::endl; auto gv = getVector(); // gv는 std::vector<int> 타입으로 추론됨 std::cout << "getVector() 결과: "; for (auto i : gv) { std::cout << i << " "; } std::cout << std::endl; // ================================================================= // 2. 제네릭 람다 (Generic Lambda) // ================================================================= std::cout << "\n--- 2. 제네릭 람다 ---" << std::endl; // C++14부터 람다 함수의 매개변수에도 auto를 사용할 수 있습니다. // 이는 이 람다 함수가 템플릿처럼 동작하게 만들어 줍니다. auto generic_lambda = [](auto a, auto b) { return a + b; }; // 다양한 타입으로 람다 함수를 호출할 수 있습니다. std::cout << "generic_lambda(10, 10) 결과 (int): " << generic_lambda(10, 10) << std::endl; std::cout << "generic_lambda(3.14, 2.71) 결과 (double): " << generic_lambda(3.14, 2.71) << std::endl; // ================================================================= // 3. decltype(auto) : 정확한 타입 추론 // ================================================================= std::cout << "\n--- 3. decltype(auto) ---" << std::endl; int x = 10; int& ref = x; // ref는 x에 대한 참조(별명) // 가. 'auto'의 타입 추론 방식 (참조 속성 제거) std::cout << "가. 'auto'는 참조(reference) 속성을 제거합니다." << std::endl; auto a = ref; // ref는 int& 타입이지만, auto는 참조 속성을 제거하고 int 타입으로 추론. // 따라서 'a'는 x의 값을 복사한 새로운 int 변수가 됩니다. a = 20; // 'a'의 값을 바꿔도 원본 'x'에는 영향이 없습니다. std::cout << " auto a = ref; a = 20; 실행 후 -> x의 값: " << x << " (변화 없음)" << std::endl; // 나. 'decltype(auto)'의 타입 추론 방식 (참조 속성 유지) std::cout << "나. 'decltype(auto)'는 참조(reference) 속성을 그대로 유지합니다." << std::endl; decltype(auto) b = ref; // decltype(ref)는 int& 이므로, 'b'는 int& 타입으로 추론됨. // 따라서 'b'는 원본 'x'를 가리키는 또 다른 참조가 됩니다. b = 30; // 'b'의 값을 바꾸면 원본 'x'가 직접 수정됩니다. std::cout << " decltype(auto) b = ref; b = 30; 실행 후 -> x의 값: " << x << " (수정됨)" << std::endl; return 0; }
- 함수 반환 타입 추론
- 제네릭 람다 - [](auto param){ ... }: 람다 함수의 매개변수 타입에 auto를 사용할 수 있게 되었다.
- decltype(auto) - decltype의 규칙을 따라 타입을 추론합니다. 즉, 표현식의 타입과 속성(참조, const 등)을 정확하게 그대로 유지한다.
C++11부터 도입된 문법으로, 배열이나 컨테이너(vector, list 등)의 요소를 하나씩 꺼내어 반복할 때 사용
#include <iostream> #include <vector> #include <string> // 벡터의 현재 상태를 출력해주는 도우미 함수 void printVector(const std::string& message, const std::vector<int>& vec) { std::cout << message << "[ "; for (int elem : vec) { std::cout << elem << " "; } std::cout << "]" << std::endl; } int main() { std::vector<int> v = { 1, 2, 3, 4, 5 }; printVector("초기 벡터 상태: ", v); std::cout << "\n==================================================" << std::endl; // --- 1. 전통적인 C-스타일 for문 (인덱스 기반) --- std::cout << "1. 전통적인 C-스타일 for문 (인덱스 기반)" << std::endl; for (int i = 0; i < v.size(); ++i) { std::cout << "v[" << i << "] = " << v[i] << std::endl; } std::cout << "==================================================" << std::endl; // --- 2. 반복자(Iterator)를 사용하는 for문 --- std::cout << "2. 반복자를 사용하는 for문" << std::endl; for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) { std::cout << "iterator가 가리키는 값: " << *it << std::endl; } std::cout << "==================================================" << std::endl; // --- 3. 범위 기반 for문 (C++11 이상) --- std::cout << "3. 범위 기반 for문 (C++11 이상)" << std::endl; // 가. 값 복사 (Call by Value) std::cout << "\n 가. 값에 의한 복사 (for (int i : v))" << std::endl; for (int i : v) { i += 1; // 'i'는 벡터 원소의 복사본이므로, 이 연산은 원본 벡터 'v'에 영향을 주지 않음 } printVector(" -> 값 복사 루프 실행 후 벡터 (변화 없음): ", v); // 나. 참조 (Call by Reference) std::cout << "\n 나. 참조에 의한 접근 (for (int& i : v))" << std::endl; for (int& i : v) { i += 1; // 'i'는 벡터 원소 자체를 가리키는 참조이므로, 원본 벡터 'v'의 값이 직접 수정됨 } printVector(" -> 참조 루프 실행 후 벡터 (원본 수정됨): ", v); // 다. 읽기 전용 참조 (const reference) - 가장 권장되는 읽기 방식 std::cout << "\n 다. 읽기 전용 (for (const auto& i : v))" << std::endl; std::cout << " (수정된 벡터의 내용을 효율적이고 안전하게 출력)" << std::endl; for (const auto& i : v) { // i += 1; // const 참조이므로 컴파일 에러 발생! 값을 수정할 수 없음. std::cout << " 읽어온 값: " << i << std::endl; } std::cout << "==================================================" << std::endl; return 0; }
- 전통적인 for문 : for (int i = 0; ...) 형태로, 인덱스 변수 i를 직접 제어
- 반복자(Iterator)를 사용하는 for문 : 컨테이너의 시작(begin())과 끝(end())을 가리키는 포인터와 유사한 '반복자'를 사용
- 범위 기반 for문 : for (변수 선언 : 컨테이너) 형태로, 컨테이너의 모든 원소를 처음부터 끝까지 순회하는 가장 간결하고 현대적인 방법