Auto 키워드
auto 키워드는 선언된 대상의 초기화 표현을 컴파일러에게 추론을 지시합니다.
auto를 사용할 때 const, volatile, 포인터(*), 참조(&), rvalue 참조 지정자를 함께 사용 할 수 있습니다.
auto의 장점
auto를 사용하면 복잡한 타입의 변수를 쉽게 선언 할 수있습니다.
예를 들어서 멤버 포인터, 함수 포인터, 템플릿 형식을 포함하는 초기화 표현식을 선언 할 때 유용합니다.
class Math
{
public:
int Sum(int left, int right)
{
return left + right;
}
};
int Sum(int a, int b)
{
return a + b;
}
// 함수 포인터의 타입
int (*SumFunc1)(int, int) = Sum;
// 함수 포인터의 타입을 auto를 사용
auto SumFunc2 = Sum;
// 멤버 함수 포인터 타입
Math math;
int (Math:: * MathSum1)(int,int) = &Math::Sum;
(math.*MathSum1)(5, 5);
// 멤버 함수 포인터 타입을 auto 사용
auto MathSum2 = &Math::Sum;
(math.*MathSum2)(5, 5);
std::map<int, int> mapContainer;
// 템플릿 형식의 타입
std::map<int, int>::iterator it1 = mapContainer.begin();
// 템플릿 형식의 타입을 auto를 사용
auto it2 = mapContainer.begin();
예제를 보시면 함수 포인터나 템플릿 타입의 경우 작성이 어려우며 오타가 날 가능성이 높습니다.
auto를 사용하면 코드를 간결하게 유지 가능하기 때문에 auto를 사용할 것을 권장합니다.
std::map<int, int> mapContainer;
std::vector<int> vecContainer;
// auto it = mapContainer.begin()에서
// 아래와 같이 변경되어도 코드의 큰 수정 없이 사용 할 수 있습니다.
auto it = vecContainer.begin();
또한 auto의 대상이 변경되어도 최소한의 수정으로 구현이 가능하도록 지원합니다.
auto사용시 참조 및 cv-qualifiers 삭제
auto를 사용해서 타입을 추론 할 때 참조 및 const 지정자, volatile 지정자가 삭제 되는 것에 유의 해야 합니다.
cout << "[auto] Check reference "<< endl;
int cnt = 10;
int& cntRef = cnt;
// auto로 int& 타입을 추론하면 int
auto Auto_cntRef = cntRef;
Auto_cntRef += 5;
cout << " Auto_cntRef = " << Auto_cntRef << endl;
cout << " cntRef = " << cntRef << endl;
cout << "[auto] Check const " << endl;
const int const_cnt = 100;
// auto로 const int 타입을 추론하면 int
auto Auto_const_cnt = const_cnt;
Auto_const_cnt++;
cout << " Auto_const_cnt = " << Auto_const_cnt << endl;
cout << "[auto &] Check " << endl;
cnt = 20;
// auto & 명시적으로 지정하면 참조 타입으로 추론
auto & Auto_countRef_ref = cnt;
Auto_countRef_ref += 5;
cout << " Auto_countRef_ref = " << Auto_countRef_ref << endl;
cout << " cnt = " << cnt << endl;
cnt = 30;
// const auto 명시적으로 지정하면 const 타입으로 추론
const auto Auto_countRef_const = cnt;
Auto_countRef_const++; // error! const 변수를 수정할 수 없습니다.
위의 예제를 보면 Auto_cntRef의 경우 auto를 사용해서 int & 타입을 추론 했는데 int로 추론되었습니다.
auto를 사용해서 추론시 참조가 삭제되는 것을 확인 할 수 있습니다. 아래의 Auto_const_cnt의 경우에도
추론시 const가 삭제되는 것을 확인 할 수 있습니다.
auto를 통해서 참조나 const, volatile 지정자를 설정하려면 명시적으로 "auto &", "const auto", "volatile auto"로
지정 해야 합니다. 처음 auto를 접할 때 자주 실수하는 부분이니 명확히 이해하고 넘어가는 것이 중요합니다.
class Employee
{
public:
void GiveSalary(int m)
{
m_Money += m;
}
int GetMoney()
{
return m_Money;
}
private :
int m_Money;
};
// 월말에 직원들에게 월급을 지급합니다.
void EndMonth()
{
std::vector<Employee> vecEmployees;
// .. 생략
// for (auto& employee : vecEmployees) 으로 수정해야 정상적으로 지급됩니다.
// for (auto employee : vecEmployees) 값 복사가 발생합니다.
for (auto employee : vecEmployees)
{
// 직원들을 순회하면 만원씩 지급합니다.
employee.GiveSalary(10000);
}
}
위의 예제는 for문 수행시 auto를 사용하는 부분인데 auto에 &을 누락하여 값 복사가 발생되는 로직입니다.
auto 연역 규칙
auto의 일반적인 연역 규칙은 템플릿 인자의 연역 규칙과 거의 일치 합니다.
아래에는 auto를 통해서 특별하게 추론되는 연역에 대해서 설명 드립니다.
보편 참조
auto && 형태로 선언을 하게 되면 해당 형식은 컴파일러에 의해서 보편 참조로 추론됩니다.
보편 참조란 문맥에 따라서 다양하게 형식 연역이 되는 타입을 뜻 합니다.
const int i = 10;
auto&& var1 = i; // 보편참조 = const int &으로 연역
const auto&& var2 = i; // const auto&&은 보편 참조 아님 error
auto&& var3 = 12; // 보편 참조 = int && 으로 연역
예제를 보면 var1은 const int 타입인 i를 전달 받아서 const int &으로 추론됩니다.
var2은 const auto && 선언되었기 때문에 const에 의해서 보편 참조가 아닌 const rvalue 참조로 인식됩니다.
var3은 int 상수 값을 전달받아서 int &&로 연역됩니다.
문자열 연역 규칙
문자열을 auto로 선언해서 타입을 추론하면 const char * 타입으로 연역되는 것을 확인 할 수 있습니다.
하지만 auto &로 선언해서 타입을 추론하면 const char &[크기] 형태로 연역되는 것을 확인 할 수 있습니다.
const char name[] = "ahn jung woong";
auto var1 = name;
auto & var2 = name;
초기화 리스트 연역 규칙
초기화 리스트에 auto를 사용하게 되면 사용자의 바램과는 다르게 동작 할 수 있습니다.
// int 로 연역됨
auto var1 = 2;
// std::initializer_list<int>로 연역됨
auto var2 = { 2 };
// error 발생 var2는 int가 아님
var2 = var1;
예제의 var2에 auto를 선언한 사용자는 int로 추론 되기를 원하지만 std::initializer_list<int>로 추론되기 때문에
int처럼 사용 되지 않습니다.
Decltype
decltype 타입 지정자는 명시된 대상의 타입을 산출합니다.
int cnt = 100;
int& refcnt = cnt;
// 컴파일러는 decltype(refcnt) = int &로 추론합니다.
decltype(refcnt) decl_refcnt = refcnt;
refcnt += 5;
std::cout << "cnt = " << cnt << endl;
std::cout << "decl_refcnt = " << decl_refcnt << endl;
// 컴파일러는 decltype(const_cnt) = const int 로 추론합니다.
const int const_cnt = cnt;
decltype(const_cnt) decl_const_cnt = const_cnt;
// 에러 const int 수정 불가
decl_const_cnt++;
decltype(대상) 형태를 제공하면 대상의 타입을 추론해서 리턴합니다.
struct A
{
public:
int a;
};
{
const A x;
decltype(x.a) AxEntity = 10;
// decltype(x.a) => int로 추론
AxEntity = 100;
decltype((x.a)) AxExpression = 10;
// decltype((x.a)) => const int로 추론
AxExpression = 100;
}
decltype은 한가지 규칙이 있는데요 decltype 호출시 내부 대상을 괄호로 감싸면 표현식으로 추론합니다.
괄호가 없다면 대상의 객체 자체의 타입을 추론합니다.
const A로 선언된 x의 a 멤버변수를 decltype으로 지정하는 예제를 볼수 있습니다.
decltype 선언 구조 | 내 용 |
decltype(대상) |
대상의 객체로 인식해서 추론합니다. 예제에서는 x.a를 객체로 인식하여 a가 선언된 타입인 int로 추론됩니다. |
decltype((대상)) |
대상을 괄호로 한번 감싸면 표현식으로 인식해서 추론합니다. 예제에서는 x.a를 표현식으로 인지해서 x의 객체가 const A이기 때문에 (x.a)는 const int로 추론됩니다. |
지원
decltype은 c++11, visual studio 2010에서부터 지원됩니다.
decltype(auto)은 c++14, visual studio 2015부터 지원됩니다.
Auto키워드와 같이 Template에서 사용
decltype 타입 지정자는 auto 키워드와 함께 사용하면 템플릿 라이브러리를 작성할 때 유용하게 사용 할 수 있습니다.
템플릿 인자의 타입을 기반으로 리턴 값 타입이 정해지는 템플릿 함수에서 auto와 decltype 키워드를 같이 사용해
리턴 타입을 정의 할 수 있습니다.
예제를 통해서 설명 드리겠습니다.
// c++ 11에서의 decltype 구현
template<typename T1, typename T2>
auto TemplatePlus(T1&& right, T2&& left) -> decltype(right + left)
{
return right + left;
}
// c++ 14에서의 decltype 구현
template<typename T1, typename T2>
decltype(auto) TemplatePlus(T1&& right, T2&& left)
{
return right + left;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 100;
// TemplatePlus(10.3, a) 리턴 타입은 double로 추론됩니다.
auto value = TemplatePlus(10.3, a);
}
// visual studio 2019에서는 이렇게 선언해도 수행가능
template<typename T1, typename T2>
auto TemplatePlus(T1&& right, T2&& left)
{
return right + left;
}
예제를 보면 TemplatePlus 템플릿 함수는 템플릿 인자 T1, T2에 의해서 리턴값이 정해지는 함수입니다.
decltype과 auto를 통해서 컴파일 타임에 T1, T2로 구성된 리턴 값의 타입을 추론하도록 컴파일러에게
지시할 수 있습니다.
예제를 보면 c++11과 c++14에서의 구현 모습이 다른 것을 확인 할 수 있습니다.
c++ 11에서는 명시적으로 리턴 타입의 표현식을 후행 반환 타입에 기입 해야 했는데
c++ 14부터는 decltype에 auto를 추가해서 간결하게 사용가능하도록 지원되었습니다.
decltype 키워드는 auto와 함께 템플릿 함수의 리턴값을 추론하는데 많이 사용되고 있습니다.
'c++ > modern c++' 카테고리의 다른 글
[c++17] string_view (0) | 2020.03.21 |
---|---|
[c++] constexpr 키워드 (0) | 2020.03.19 |
[c++] std::move와 std::forward (4) | 2020.03.07 |
[c++] Move semantics (0) | 2020.03.07 |
[c++] 람다(Lambda) 표현식 (0) | 2020.03.04 |