std:: enable_if
| Type traits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Compile-time rational arithmetic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Compile-time integer sequences | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
(C++14)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
헤더에 정의됨
<type_traits>
|
||
|
template
<
bool
B,
class
T
=
void
>
struct enable_if ; |
(C++11부터) | |
만약
B
가
true
라면,
std::enable_if
는
T
와 동일한
type
공용 멤버 typedef를 가집니다; 그렇지 않으면, 멤버 typedef가 존재하지 않습니다.
이 메타함수는 C++20의 concepts 이전에 SFINAE 를 활용하는 편리한 방법으로, 특히 타입 특성에 기반하여 candidate set 에서 함수를 조건부로 제거하여 서로 다른 타입 특성에 기반한 별도의 함수 오버로드나 특수화를 가능하게 합니다.
std::enable_if
는 다음과 같은 다양한 형태로 사용할 수 있습니다:
- 추가 함수 인수로 (대부분의 연산자 오버로드에는 적용되지 않음),
- 반환 타입으로 (생성자와 소멸자에는 적용되지 않음),
- 클래스 템플릿 또는 함수 템플릿 매개변수로.
프로그램이
std::enable_if
에 대한 특수화를 추가하는 경우, 동작은 정의되지 않습니다.
목차 |
멤버 타입
| 유형 | 정의 |
type
|
B
의 값에 따라
T
이거나 해당 멤버가 없음
|
헬퍼 타입
|
template
<
bool
B,
class
T
=
void
>
using enable_if_t = typename enable_if < B,T > :: type ; |
(C++14부터) | |
가능한 구현
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
참고 사항
흔히 하는 실수는 기본 템플릿 인자만 다른 두 함수 템플릿을 선언하는 것입니다. 이는 동일한 함수 템플릿의 재선언으로 처리되기 때문에 작동하지 않습니다(기본 템플릿 인자는 함수 템플릿 동등성 에서 고려되지 않습니다).
/* 잘못된 예시 */ struct T { enum { int_t, float_t } type; template<typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value>> T(Integer) : type(int_t) {} template<typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value>> T(Floating) : type(float_t) {} // 오류: 재정의로 처리됨 }; /* 올바른 예시 */ struct T { enum { int_t, float_t } type; template<typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true> T(Integer) : type(int_t) {} template<typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true> T(Floating) : type(float_t) {} // 정상 };
네임스페이스 범위 함수 템플릿의 상수 템플릿 매개변수 타입에서
enable_if
를 사용할 때는 주의해야 합니다. Itanium ABI와 같은 일부 ABI 사양은 맹글링(mangling)에 상수 템플릿 매개변수의 인스턴스화 종속 부분을 포함하지 않아, 두 개의 서로 다른 함수 템플릿 특수화가 동일한 맹글링 이름을 갖게 되어 오류로 함께 링크될 수 있습니다. 예를 들어:
// 첫 번째 번역 단위 struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value1, int> = 0> void func() {} // #1 template void func<X>(); // #2 // 두 번째 번역 단위 struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value2, int> = 0> void func() {} // #3 template void func<X>(); // #4
함수 템플릿 #1과 #3은 서로 다른 시그니처를 가지며 별개의 템플릿입니다. 그럼에도 불구하고, #2와 #4는 서로 다른 함수 템플릿의 인스턴스화임에도 불구하고 Itanium C++ ABI에서
맹글링된 이름
이 동일합니다(
_Z4funcI1XLi0EEvv
). 이는 링커가 이들을 오류로 동일한 엔티티로 간주하게 됨을 의미합니다.
예제
#include <iostream> #include <new> #include <string> #include <type_traits> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, 반환 타입을 통해 활성화됨 template<class T> typename std::enable_if<std::is_trivially_default_constructible **번역 결과:** std::is_trivially_default_constructible **번역 설명:** - HTML 태그와 속성은 그대로 유지 - C++ 관련 용어(`std::is_trivially_default_constructible`)는 번역하지 않음 - 링크 구조와 클래스 속성 완전히 보존 - 전문적인 기술 문서 번역 기준 준수<T>::value>::type construct(T*) { std::cout << "trivially default constructible T의 기본 생성\n"; } // 위와 동일 template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type construct(T* p) { std::cout << "비트리비얼리 기본 생성 가능하지 않은 T의 기본 생성\n"; ::new(detail::voidify(p)) T; } // #2 template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::value> // 헬퍼 타입 사용 construct(T* p, Args&&... args) { std::cout << "T를 operation으로 생성 중\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, 매개변수를 통해 활성화됨 template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::value >::type* = 0) { std::cout << "trivially destructible T를 파괴하는 중\n"; } // #4, 상수 템플릿 매개변수를 통해 활성화됨 template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true> void destroy(T* t) { std::cout << "비-자명 소멸자를 가진 T를 파괴하는 중\n"; t->~T(); } // #5, 타입 템플릿 매개변수를 통해 활성화됨 template<class T, typename = std::enable_if_t<std::is_array<T>::value>> void destroy(T* t) // 참고: 함수 시그니처는 수정되지 않음 { for (std::size_t i = 0; i < std::extent<T>::value; ++i) destroy((*t)[i]); } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value>> void destroy(T* t) {} // 오류: #5와 동일한 시그니처를 가짐 */ // A의 부분 특수화는 템플릿 매개변수를 통해 활성화됩니다 template<class T, class Enable = void> class A {}; // primary template template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {}; // 부동 소수점 타입 전문화 int main() { union { int i; char s[sizeof(std::string)]; } u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u), "안녕하세요"); destroy(reinterpret_cast<std::string*>(&u)); A<int>{}; // OK: 기본 템플릿과 일치함 A<double>{}; // OK: 부분 특수화와 일치함 }
출력:
default constructing trivially default constructible T destroying trivially destructible T constructing T with operation destroying non-trivially destructible T
참고 항목
|
(C++17)
|
void 가변 인자 앨리어스 템플릿
(앨리어스 템플릿) |