SFINAE
SFINAE(대체 실패는 오류가 아님, Substitution failure is not an error)는 C++에서 템플릿 매개변수에 자료형이나 값을 넣을 수 없어도 오류가 발생하지 않는 상황을 말한다. 이를 이용한 프로그래밍 테크닉을 설명하기 위해 SFINAE라는 줄임말을 최초로 사용한 사람은 David Vandevoorde이다.[1]
더 자세히 설명하자면, 함수 오버로드 후보 집합을 만들 때 그 중에는 템플릿 매개 변수에 자료형이나 값이 대입된 경우가 있을 수 있다. 이 대체 과정에서 발생한 오류가 C++ 표준이 허용하는 것이라면, 컴파일러는 컴파일 오류를 보고하지 않고, 후보 집합에서 그 후보를 제외한다.[2] 만약 하나 이상의 후보가 남고 오버로드 결정(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[3]과 같은 방법으로 SFINAE를 사용하였다.
각주
- ↑ Vandevoorde, David; Nicolai M. Josuttis (2002). 《C++ Templates: The Complete Guide》. Addison-Wesley Professional. ISBN 0-201-73484-2.
- ↑ International Organization for Standardization. "ISO/IEC 14882:2003, Programming languages — C++", § 14.8.2.
- ↑ Boost Enable If
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.