C++/템플릿(Template)

C++ 템플릿(Template): SFINAE

kim선달 2020. 11. 1. 19:15
Select Language

C++ Template: SFINAE

 

Substitution Failure INot AError

SFINAE는 C++ 템플릿 프로그래밍에서 자주 쓰이는 기법입니다.

컴파일러가 알맞은 함수를 추론(deduce) 하는 원리를 이용해, 받는 타입을 제한하거나 타입에 따라 행동을 다르게 할 때 주로 사용하게 됩니다.

SFINAE is a technique that is often used in C++ Template Programming.

It restrains function parameter types by using the mechanism of how the compiler deduces appropriate overloaded functions and calling the appropriate (overloaded)function you intend.

 


그 전에, 유사한 상황인 함수 오버로딩(function overloading)을 먼저 한번 보겠습니다.

Before we begin talking about SFINAE, let's take a look at a similar situation.

 

#include <iostream>

void foo(double x){
    std::cout << x << " is double!" << std::endl;
}

void foo(int x){
    std::cout << x << " is int!" << std::endl;
}

int main(){
    double a = 3.14;
    int b = 1;
    
    foo(a);
    foo(b);
    
    return 0;
}

 

출력 결과는

The outputs are:

3.14 is double!
1 is int!

 

이미 C++ 을 잘 아신다면 결과는 너무 뻔합니다.

컴파일러가 인자에 따라 함수를 자동으로 추론해서 알맞은 함수를 호출한 것이죠.

The result is very obvious.

The compiler deduces the overloaded function foo from its parameter a and b


설명

Explanation

템플릿 프로그래밍도 보다 비슷하지만, 일반적인 함수 오버로딩과 다른 점이 있습니다.

위의 경우에, double 타입을 인자로 받는 foo 를 없에 버리면 어떻게 될까요?

narrowing conversion 을 통해 warning 메세지는 뜰 지언정, 컴파일은 잘 됩니다.

 

반면 템플릿 프로그래밍에서는 이 기법을 사용하여 타입에 제한을 걸어주면, 알맞지 않은 타입이 인자로 전달된다면 대입 실패 (substitution failure)가 나게 됩니다.

다시 말하면, 오버로딩된 함수 2개가 있는데, 하나는 double 타입을 받을 시 문법 오류가 나게 되고, 나머지 하나는 그렇지 않다면 컴파일러는 자동으로 오류가 나지 않는 함수를 알맞은 함수로 생각하게 된다는 것입니다.

이처럼 문법 오류를 의도했지만, 기법의 한 종류이므로 에러가 아니다(not an error) 라고 해서 SFINAE 라고 부르게 됩니다.

참으로 괴랄한 네이밍 센스입니다... 어떻게 발음하는지도 모르겠군요.

아시는 분이 있다면 댓글로...

 

물론 오류가 나지 않는 함수가 2개 이상이면 추론이 불가능해져 컴파일러는 에러를 뱉게 됩니다.

What SFINAE is trying to do is the same as above, but there are some differences.

In the above example, what will happen if you remove the function that takes double as a parameter?

There can be a narrowing conversion(double-to-int) warning, but it compiles well. 

 

But in SFINAE, if you pass other types to a function that is not what you intended, it will make syntax failure(and resulting in a compilation error if compiled).

In other words, if there's a two function and one makes syntax failure and the other doesn't, the compiler will guess that the non-syntax failing function is correct and call that one.

Like this, you intended Substitution Failure but it's to call the appropriate overloaded function.

So it is Not An Error(since it compiles well).

How odd the name is... can you guess how to pronounce it? :-s

 

Of course, SFINAE will fail in the below cases.

    - All function are syntax failing(for the given type)

    - More than 2 functions are non-syntax failing

 


예시

Example

C++ 11

#include <iostream>
#include <type_traits>

template<typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
void foo(T x){
  std::cout << x << " is floating point!" << std::endl;
}

template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(T x){
  std::cout << x << " is integral!" << std::endl;
}

int main(){
  double a;
  int b;

  foo(a);
  foo(b);

  return 0;
}

 

 

C++ 14

  std::enable_if_t 가 추가되었습니다! 15 글자나 줄어들게 되었습니다

  std::enable_if_t is added! 15 less words

#include <iostream>

template<typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void foo(T x){
  std::cout << x << " is floating point!" << std::endl;
}

template<typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
void foo(T x){
  std::cout << x << " is integral!" << std::endl;
}

int main(){
  double a;
  int b;

  foo(a);
  foo(b);

  return 0;
}

 

위 코드에서 처음의 foo 함수를 삭제하게 되면, foo(a)는 문법 오류이기 때문에 컴파일 에러가 나게 됩니다.

이처럼 std::enable_if 를 이용하여 타입을 제한하는것을 SFINAE 라고 합니다.

 

 

이와 동일한 기능을 꼬리표 분배(Tag Dispatchig) 방법을 이용해 구현 할 수 도 있습니다.

해당 방법은 cppmagister.tistory.com/26 를 참고해 주세요.

In the above example, if we remove the first foo, then calling foo(a) is a syntax failure and it won't compile.

Like this, restricting types with std::enable_if to call appropriate overloaded function is called SFINAE.

 

 

You can do the same intend(deducing appropriate overloaded functions) with Tag Dispatching.

 

 

질문과 피드백, 댓글은 작성자에게 큰 힘이 됩니다!

Questions, Feedbacks, and comments are very helpful to me!