C++ 표준에 정의된 'auto' 키워드는 원래 의미와 수정된 의미가 존재합니다. C++11 이전에는 'auto'는 자동 저장소 클래스에 있는 변수나 지역 변수를 선언하는 역할을 했으나, C++11부터 'auto' 키워드는 초기화식을 통해 형식이 추론되는 변수를 선언하는 역할을 합니다. 이러한 차이로 인해 'auto' 키워드는 C++ 버전에 따라 다르게 동작할 수 있습니다.
#include <iostream>
using namespace std;
int main() {
int exampleInt = 10;
int& exampleIntRef = exampleInt;
auto autoVar = exampleIntRef;
exampleIntRef = 11;
cout << exampleInt << " ";
autoVar = 12;
cout << exampleInt << endl;
}
위와 같은 경우, 'exampleInt'를 참조하는 변수를 받았으므로 11 12가 출력됩니다.
#include <initializer_list>
int main() {
// std::initializer_list<int>
auto listA = { 1, 2 };
// std::initializer_list<int>
auto listB = { 3 };
// int
auto varC{ 4 };
// 에러: 'auto'의 형식을 초기화식에서 추론할 수 없음
auto listD = { 5, 6.7 };
// 에러: 후행 초기화 문맥에서 'auto'의 형식은 단일 초기화식에서만 추론 가능
auto listE{ 8, 9 };
return 0;
}
'listD'와 같이 식을 추론할 수 없거나 'listE'와 같이 여러 변수를 포함하여 초기화하는 방법은 오류를 발생시킵니다.
#include <iostream>
using namespace std;
int increment(int i) {
return i + 1;
}
int main() {
auto boolVar = true;
auto charVar = 'X';
auto wideCharVar = L'X';
auto stringVar = "문자열";
auto intVar = 26;
auto doubleVar = 3.12;
auto longVar = 10000000000;
auto& intRef = intVar; // 참조
auto* intPtr = &intVar; // 포인터
cout << "값\t\t크기\t\t자료형" << endl;
cout << boolVar << "\t\t" << sizeof(boolVar) << "\t\t" << typeid(boolVar).name() << endl;
cout << charVar << "\t\t" << sizeof(charVar) << "\t\t" << typeid(charVar).name() << endl;
cout << wideCharVar << "\t\t" << sizeof(wideCharVar) << "\t\t" << typeid(wideCharVar).name() << endl;
cout << stringVar << "\t\t" << sizeof(stringVar) << "\t\t" << typeid(stringVar).name() << endl;
cout << intVar << "\t\t" << sizeof(intVar) << "\t\t" << typeid(intVar).name() << endl;
cout << doubleVar << "\t\t" << sizeof(doubleVar) << "\t\t" << typeid(doubleVar).name() << endl;
cout << longVar << "\t" << sizeof(longVar) << "\t\t" << typeid(longVar).name() << endl;
auto incrementFunc = increment; // 함수 포인터
cout << incrementFunc(3) << endl;
auto printHello = [] { cout << "hello" << endl; }; // 매개변수 없는 함수, Lambda
printHello();
auto incrementBy1 = [](int i) { return i + 1; }; // 매개변수 있는 함수, Lambda
cout << incrementBy1(3) << endl;
int count = 0;
auto addToCount = [&](int i) { count += i; }; // 함수 자체(함수 내부에서 외부 변수 참조 시 & 대입), Lambda
auto returnByReference = [](int* i) -> int& { return *i; }; // 함수 자체(참조자를 리턴하려는 경우), Lambda
}
그러나 몇 가지 경우에서는 'auto'를 사용할 수 없습니다.
void add(auto a, auto b) {} // 오류
struct {
auto memberB;
}
class Person {
auto name;
}
'auto'를 사용하는 함수에서 후행 반환 형식을 지정할 수 있습니다. 후행 반환 형식을 지정하면 함수의 반환 형식을 명시적으로 지정할 수 있습니다.
auto returnDouble() { // 에러: 후행 반환 형식을 지정하지 않음
return 3.2;
}
auto returnDouble() -> double { // 정상: 함수 뒤에 후행 반환 형식을 지정
return 3.2;
}
후행 반환 형식을 지정하면 함수의 반환 형식을 명시적으로 지정할 수 있으며, 이것은 함수 템플릿과 함께 사용하면 유용합니다.
template<typename Param1, typename Param2>
auto add(Param1 p1, Param2 p2) -> decltype(p1 + p2) {
return p1 + p2;
}
위의 'add' 함수에서는 'int'형과 'double'형이 매개변수로 들어오면 'decltype'에서 'p1'과 'p2'를 더해서 'double'이 되고, 'auto'는 자동으로 'double
' 형태로 반환됩니다. 이렇게 'decltype'을 이용하여 반환 형식을 예측 가능하게 만들 수 있습니다.
이러한 방법은 함수 템플릿을 사용하는 상황에서 인수의 자료형 순서를 알 수 없는 경우에 유용합니다. 'auto'와 'decltype'을 함께 사용하여 더 큰 자료형을 반환하고자 할 때, 'decltype'을 이용하여 'double' 형식을 반환할 수 있도록 만들 수 있습니다.
만약 'auto'를 사용하지 않는다면 사용자는 모든 경우의 자료형을 갖는 함수를 작성해야 합니다.
int add(int a, int b);
double add(int a, double b);
double add(double a, int b);
double add(double a, double b);
더 다양한 자료형을 다루어야 하는 경우, 함수 템플릿과 'auto'를 함께 사용하면 하나의 함수 작성으로 끝낼 수 있습니다.
'auto'를 사용하면 코드 작성이 더 간편해지고 개발자의 생산성을 높일 수 있습니다. 그러나 무분별한 사용은 자료형 파악을 어렵게 하고 코드의 가독성을 떨어뜨릴 수 있습니다. 따라서 코드 컨벤션을 준수하면서 적절한 상황에서 'auto'를 사용하면 아름다운 코드를 작성할 수 있을 것입니다.