written by yechoi

[C++] SFINAE 와 enable_if의 사용법 본문

Born 2 Code/C, C++

[C++] SFINAE 와 enable_if의 사용법

yechoi 2021. 8. 24. 17:17
반응형

SFINAE란

Substition Failure Is Not An Error의 약자이다. 템플릿 인자 추론에서, C++ 컴파일러는 적합한 오버로딩 함수를 찾는다. 그 과정에서 함수 템플릿을 인스턴스화할 때, 인자나 결과가 유효하지 않다면 '컴파일 에러'를 뱉는 대신에 오버로드 결과물에서 해당 인스턴스화한 것을 지운다.

 

무슨 의미인지는 코드 예시를 보며 확인해본다.

long multiply(int i, int j) { return i * j; }

template <class T>
typename T::multiplication_result multiply(T t1, T t2)
{
  return t1 * t2;
}
int main(void)
{
  multiply(4,5);
}

 

위 코드에서 단순 multiply 함수가 있고, 템플릿화한 함수가 있다. multiply 함수를 main에서 호출하면 첫번째 multiply 함수가 더 적합하더라도 컴파일러는 템플릿화한 함수를 인스턴스화하려고 시도한다.

이 과정에서 int::multiplication_result 라는 유효하지 않은 자료형이 생성된다. 그렇지만 SFINAE로 인해, 유효하지 않은 인스턴스화는 자동으로 무시된다.

SFINAE는 컴파일 타임에 프로퍼티를 결정하는 데 사용되고는 한다.

 

SFINAE를 가능케 하는 구문, enable-if

SFINAE를 코드로 구현할 수 있도록 하는 구문이 enable-if다. enable_if 는 아래처럼 구현될 수 있다.

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

enable_if 의 템플릿 첫번째 인자로 true 가 오면, enable_if 의 구조체 안에는 type이라는 자료형이 생긴다. 따라서 enable_if의 템플릿 첫번째 인자로 boolean을 반환하는 구문을 넣어두고 해당 구문에서 판별을 요청하면, 결과값에 따라 type이 생기거나 생기지 않는 것을 이용할 수 있다.

 

아래는 이에 대한 예시다.

template<typename T, std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(const T& bar) { isInt(); }

is_integral 은 T가 integral 계열인지 true, false로 반환한다. integral 계열일 때만 ::type 호출이 가능하다. 그러니까 integral 계열이 아니라면, 이 foo 함수는 적합하지 않은 것으로 판단하고 인스턴스화하지 않고 넘어간다.

 

참고로 템플릿의 두번째 인자인 std::enable_if<std::is_integral<T>::value, int>::type= 0 으로 디폴트 값이 정해져있다. 그러니까 두번째 인자는 굳이 입력하지 않고 foo< int >( 1 ); 이런식으로 호출이 가능하다. 디폴트 값을 설정해놓지 않았더라면, 템플릿에서는 두개의 변수를 필요로 했을 것이다.

🔗 [stackoverflow] How Does std::enable_if work?🔗 [wikibooks] SFINAE

 

How Does std::enable_if work?

I just asked this question: std::numeric_limits as a Condition I understand the usage where std::enable_if will define the return type of a method conditionally causing the method to fail to compi...

stackoverflow.com

 

반응형