C, C++/C++ 언어 / / 2024. 9. 13.

[C++] auto의 형식 추론( type deduction ) 규칙

auto의 형식 추론

auto는 컴파일러가 초기화 표현식으로부터 변수의 타입을 추론하도록 하는 키워드입니다.

물론, 변수의 타입을 추론하는데 기준이 되는 규칙이 있습니다.

정말 다행인 것은, 이 규칙이 템플릿의 형식 추론( template type deduction ) 규칙과 같다는 것입니다.

 

그리고, 템플릿의 형식 추론 규칙을 먼저 익히면, auto의 규칙을 익히는 데 아주 수월함을 느낄 수가 있습니다.

이러한 템플릿 형식 추론 규칙은 여기에서 볼 수 있습니다.

 

[C++] 템플릿의 형식 추론( template type deduction ) 규칙

형식 템플릿 매개변수( type template parameter )의 추론이 글은 다음의 템플릿 함수가 호출되었을 때, 컴파일러에 의해서 어떤 함수로 구체화되는지에 관한 내용을 설명합니다. 바로 본론으로 들어

codingembers.tistory.com

 

위 글에선, 형식을 추론하기 위해 다음의 코드들을 이용했습니다.

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++의 초기화 종류에 관한 내용은 여기에서 볼 수 있습니다.

 

[C++] 초기화( initialization )의 종류 정리

변수의 초기화C++을 사용해서, 코딩을 하다 보면 의외로 다양한 방법으로 변수를 초기화할 수 있음을 알게 됩니다.( 물론, 다른 언어도 다양한 방식의 초기화 방법을 제공합니다. ) 그런데, 대부

codingembers.tistory.com

 

위의 초기화 표현식 중에서 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++] 람다 표현식( lamda expression )에 대한 설명

람다 표현식( lamda expression )이란줄여서 람다( lamda )라고도 하는 람다 표현식은 익명의 함수 객체를 정의하고 사용하기 위한 표기법입니다.이 표현식은 간결한 기능을 구현하는데 너무 많은 손이

codingembers.tistory.com

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유