auto의 형식 추론
auto는 컴파일러가 초기화 표현식으로부터 변수의 타입을 추론하도록 하는 키워드입니다.
물론, 변수의 타입을 추론하는데 기준이 되는 규칙이 있습니다.
정말 다행인 것은, 이 규칙이 템플릿의 형식 추론( template type deduction ) 규칙과 같다는 것입니다.
그리고, 템플릿의 형식 추론 규칙을 먼저 익히면, auto의 규칙을 익히는 데 아주 수월함을 느낄 수가 있습니다.
이러한 템플릿 형식 추론 규칙은 여기에서 볼 수 있습니다.
위 글에선, 형식을 추론하기 위해 다음의 코드들을 이용했습니다.
template < typename T >
void func( ParamType param){};
func( expr );
표현식인 expr로부터 ParamType을 추론하고, 이를 통해 템플릿의 형식 T을 추론했습니다.
이 방식을 auto 변수의 형식을 추론할 때도 사용할 수 있습니다.
아래와 같은 문장이 있을 때, 다음과 같은 타입의 일치를 생각할 수 있습니다.
auto& func = 10;
위의 표현식인 10은 expr에 해당합니다.
그리고, 변수의 형식 지정자( type specifier )인 auto&는 ParamType에 해당합니다.
마지막으로, auto가 템플릿의 형식인 T에 해당합니다.
이러한 대응관계로 위의 식을 바라보면, 이 식은 다음과 같이 변경할 수 있습니다.
template < typename T >
void func( T& param ){};
func( 10 );
그리고, 이러한 템플릿의 타입 T는 템플릿 타입 추론 규칙 1) 번에 의해, int 타입으로 추론할 수 있습니다.
따라서, 원래 문장은 다음과 같이 추론됩니다.
int& func = 10; // auto 타입 추론 결과
이제, 템플릿 형식 추론 규칙 세 가지를, auto 형식을 추론하는데 적용만 시키면 될 것입니다.
1) 형식 지정자가 참조 형식이지만 보편 참조는 아닐 경우
이런 경우 변수의 초기화는 다음과 같은 형태로 수행됩니다.
auto& var = expr;
이 선언은 다음의 템플릿 함수로 대응시켜, 변수의 타입을 추론할 수 있습니다.
template < typename T >
void val( T& param ){};
따라서, 아래와 같이 선언된 변수의 타입을 추론할 수 있습니다.
auto& x = 27; // 형식 지정자는 int&, auto는 int
const auto& rx = x; // 형식 지정자는 const int&, auto는 int
2) 형식 지정자가 보편 참조( universal reference )인 경우
이런 경우 변수의 초기화는 다음과 같은 형태로 수행됩니다.
auto&& var = expr;
이 선언은 다음의 템플릿 함수로 대응시켜, 변수의 타입을 추론할 수 있습니다.
template < typename T >
void val( T&& param ){};
따라서, 아래와 같이 선언된 변수의 타입을 추론할 수 있습니다.
int x = 27; // 왼값
const cx = x; // 왼값
auto&& lx = x; // 형식 지정자는 int&
auto&& lcx = cx; // 형식 지정자는 const int&
auto&& rx = 27; // 형식 지정자는 int&&
3) 형식 지정자가 참조 형식이 아닌 경우
이런 경우 변수의 초기화는 다음과 같은 형태로 수행됩니다.
auto var = expr;
이 선언은 다음의 템플릿 함수로 대응시켜, 변수의 타입을 추론할 수 있습니다.
template < typename T >
void val( T param ){};
따라서, 아래와 같이 선언된 변수의 타입을 추론할 수 있습니다.
auto x = 27; // 형식 지정자는 int
const auto cx = x; // 형식 지정자는 const int
const char str[] = "Hello. world !";
auto cstr = str; // 형식 지정자는 const char*
void func( int a, double b ); // 함수 선언
auto f = func; // 형식 지정자는 int (*)(int, double)
템플릿 형식 추론과 auto 형식 추론의 다른 점
템플릿 형식 추론 규칙에 없는 auto 형식 추론 규칙이 있습니다.
이 규칙은 초기화 표현식이 균일 초기화 식일 때 ( 좀 더 정확하게는 copy uniform initialization인 경우), auto의 형식이 std::initializer_list로 추론된다는 것입니다.
다음의 예들은 변수를 초기화하는 데 사용되는 초기화 식들입니다.
auto val1 = 10; // 복사 초기화
auto val2( 10 ); // 직접 초기화
auto val3{ 10 }; // 균일 초기화( direct uniform )
auto val4 = { 10 }; // 균일 초기화( copy uniform )
이러한 C++의 초기화 종류에 관한 내용은 여기에서 볼 수 있습니다.
위의 초기화 표현식 중에서 val4 변수만이 initializer_list 객체로 추론됩니다.
( 나머지는 모두 int로 추론 )
std::initializer_list<int> val4 = { 27 };
그런데, 이 표현식은 템플릿 형식 추론 규칙으론 추론할 수 없습니다.
template < typename T >
void func( T& param){};
func( { 27 } ); // error !
▼출력
error: no matching function for call to 'func(<brace-enclosed initializer list>)'
이렇게 되는 이유는, { 27 } 표현식의 형식을 추론하는 데는 두 개의 타입이 필요하기 때문입니다.
하나는 std::initializer_list 타입이고, 다른 하나는 initializer_list를 채우는 원소의 타입입니다.
따라서, 위의 템플릿으로는 형식을 추론할 수 없게 되는 것입니다.
만약, 함수 템플릿이 아래와 같다면, 이 경우엔 템플릿의 형식을 추론할 수 있습니다.
template < typename T >
void func_init_list( std::initializer_list<T> param){};
func_init_list( { 27 } ); // ok
이때, 형식 템플릿 매개변수 T는 int로 추론됩니다.
하지만, C++ 14 이후에 가능해진, auto가 함수의 반환 형식이나, 람다 표현식의 매개변수에 사용되는 경우는 템플릿 형식 추론 규칙에 따라야 합니다.
그래서, 다음의 함수는 컴파일 오류를 발생시킵니다..
auto create_initializer_list(){
return { 1, 2, 3 }; // 형식을 추론할 수 없습니다.
}
함수의 매개 변수나 반환 값은 대입연산을 사용하는 초기화 방식을 사용합니다.
( 위의 "초기화의 종류 정리" 글 참조 )
따라서, 위의 반환 값을 나타내는 문장은 다음과 같이 됩니다.
auto return_value = { 1, 2, 3 }; // copy uniform initialization
그리고, 이때 적용되는 템플릿 형식 추론 규칙은, 이러한 균일 초기화 표현식으로부터 initializer_list를 추론할 수 없습니다.
마찬가지로, 다음의 람다 표현식( lamda expression )의 auto 매개변수 타입도 추론할 수 없습니다.
따라서, 오류가 발생됩니다.
vector<int> int_vec;
auto set_init_list = [&int_vec]( const auto& list){ int_vec = list; };
set_init_list( { 1, 2, 3 } ); // 형식을 추론할 수 없음. error !
람다 표현식에 관한 내용은 여기서 볼 수 있습니다.
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 이름이나 표현식의 타입을 알려주는 decltype (2) | 2024.09.13 |
---|---|
[C++] 컴파일러가 자동으로 작성하는 멤버 함수들 (2) | 2024.09.13 |
[C++] 완벽한 전달( perfect forwarding )에 대한 설명 (1) | 2024.09.08 |
[C++] 템플릿의 형식 추론( template type deduction ) 규칙 (0) | 2024.09.07 |
[C++] 범위 있는( scoped ) enum에 관하여 (0) | 2024.09.05 |