**SFINAE**(대체 실패는 오류가 아님, Substitution failure is not an error)는 C++에서 템플릿 매개변수에 자료형이나 값을 넣을 수 없어도 오류가 발생하지 않는 상황을 말한다. 이를 이용한 프로그래밍 테크닉을 설명하기 위해 SFINAE라는 줄임말을 최초로 사용한 사람은 David Vandevoorde이다.

더 자세히 설명하자면, 함수 오버로드 후보 집합을 만들 때 그 중에는 템플릿 매개 변수에 자료형이나 값이 대입된 경우가 있을 수 있다. 이 대체 과정에서 발생한 오류가 C++ 표준이 허용하는 것이라면, 컴파일러는 컴파일 오류를 보고하지 않고, 후보 집합에서 그 후보를 제외한다. 만약 하나 이상의 후보가 남고 오버로드 결정(Overload Resolution)에 성공한다면, 호출이 정상적으로 이루어진다.

예시
--

다음은 SFINAE의 기본 예시이다. 

```
struct Test {
 typedef int foo;
};

template <typename T>
void f(typename T::foo) {} // 정의 1

template <typename T>
void f(T) {} // 정의 2

int main() {
 f<Test>(10); // 정의 1을 호출한다.
 f<int>(10); // SFINAE 덕분에
 // int::foo가 없으므로 오류 없이 정의 2를 호출한다.
}

```
 위 예제에서, `f<int>`를 사용했을 때 `int` 내부에 `foo` 가 정의되어 있지 않기 때문에 `T::foo`에서 대체 오류가 발생하지만, 유효한 함수가 후보 집합에 남아있기 때문에 이 프로그램은 잘 작동한다.

이 개념은 잘못된 템플릿 선언이 사용되는 것을 막기 위해(헤더 파일이 포함되었을 때 등) 도입되었지만, 많은 개발자들이 이러한 점을 이용해서 컴파일 시에 활용할 수 있는 검사 기법을 만들어냈다. 특히 SFINAE는 템플릿을 인스턴스화할 때 템플릿 전달인자의 속성들을 정할 수 있도록 해준다.

예를 들어, SFINAE를 사용하면 주어진 자료형에 특정 typedef가 포함되어있는지 확인할 수 있다. 

```
#include <iostream>

template <typename T>
struct has\_typedef\_foobar {
 // 자료형 "yes"와 "no"는 다른 크기를 가지도록 보장되어있다.
 // 여기서 sizeof(yes) == 1 이고 sizeof(no) == 2이다.
 typedef char yes[1];
 typedef char no[2];

 template <typename C>
 static yes& test(typename C::foobar\*);

 template <typename>
 static no& test(...);

 // 만약 test<T>(nullptr)를 호출한 결과의 sizeof가 sizeof(yes)와 같다면,
 // 첫번째 오버로드가 호출된 것이고 주어진 자료형 T는 foobar라는 내부 타입을
 // 가지고 있다.
 static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};

struct foo {
 typedef float foobar;
};

int main() {
 std::cout << std::boolalpha;
 std::cout << has\_typedef\_foobar<int>::value << std::endl; // false를 출력
 std::cout << has\_typedef\_foobar<foo>::value << std::endl; // true를 출력
}

```
 `T` 내부에 자료형 `foobar`가 정의되어 있으면 첫번째 `test`가 후보로 선택되고 널포인터 상수가 성공적으로 전달된다. (이 때 호출 결과의 자료형은 `yes`이다. ) 반대의 경우, 사용할 수 있는 유일한 함수는 두번째 `test`이고 결과의 자료형은 `no`이다. 여기서 생략 부호는 아무 자료형이나 받을 수 있으면서, 변환 순위가 가장 낮기 때문에 가능한 경우 첫번째 함수가 호출되도록 만든다. 이를 통해 모호성을 없앨 수 있다.

C++11에서 더 단순하게 사용하기
-------------------

C++11에서는 위 코드를 다음과 같이 더 단순하게 사용할 수 있다. 

```
#include <iostream>
#include <type\_traits>

template <typename... Ts>
using void\_t = void;

template <typename T, typename = void>
struct has\_typedef\_foobar : std::false\_type {};

template <typename T>
struct has\_typedef\_foobar<T, void\_t<typename T::foobar>> : std::true\_type {};

struct foo {
 using foobar = float;
};

int main() {
 std::cout << std::boolalpha;
 std::cout << has\_typedef\_foobar<int>::value << std::endl;
 std::cout << has\_typedef\_foobar<foo>::value << std::endl;
}

```
 Library fundamental v2 (n4562)에서 제안된 탐지 패턴 표준을 이용하면, 위 코드를 다음과 같이 쓸 수 있다. 

```
#include <iostream>
#include <type\_traits>

template <typename T>
using has\_typedef\_foobar\_t = typename T::foobar;

struct foo {
 using foobar = float;
};

int main() {
 std::cout << std::boolalpha;
 std::cout << std::is\_detected<has\_typedef\_foobar\_t, int>::value << std::endl;
 std::cout << std::is\_detected<has\_typedef\_foobar\_t, foo>::value << std::endl;
}

```
 Boost의 개발진은 boost::enable\_if과 같은 방법으로 SFINAE를 사용하였다.

참고 문헌
-----

1. ↑  Vandevoorde, David; Nicolai M. Josuttis (2002). 《C++ Templates: The Complete Guide》. Addison-Wesley Professional. ISBN 0-201-73484-2.
2. ↑  International Organization for Standardization. "ISO/IEC 14882:2003, Programming languages — C++", § 14.8.2.
3. ↑  Boost Enable If