다음을 통해 공유


enable_if 클래스

SFINAE 오버로드 확인을 위한 형식의 인스턴스를 조건부로 만듭니다. 중첩된 typedef enable_if<Condition,Type>::type이 있고 이는 Type의 동의어입니다. Conditiontrue인 경우에만 해당됩니다.

구문

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

매개 변수

B
결과 형식의 존재를 확인하는 값입니다.

T
인스턴스화할 형식(있는 경우 B )입니다 true.

설명

trueenable_if<B, T> 경우 B 동의어T인 "type"이라는 중첩된 typedef가 있습니다.

falseenable_if<B, T> 경우 B "type"이라는 중첩된 typedef가 없습니다.

다음 별칭 템플릿이 제공됩니다.

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

C++에서 템플릿 매개 변수의 대체 실패는 그 자체로 오류가 아닙니다. 이를 SFINAE라고 합니다(대체 실패는 오류가 아님). 일반적으로 enable_if는 오버로드 확인에서 후보를 제거하는 데 사용됩니다. 즉, 오버로드 집합을 선별합니다. 따라서 다른 정의를 사용하기 위해 정의 하나가 거부될 수 있습니다. 이는 SFINAE 동작을 따릅니다. SFINAE에 대한 자세한 내용은 Wikipedia에서 대체 실패가 오류가 아님을 참조하세요.

다음은 4가지 예제 시나리오입니다.

  • 시나리오 1: 함수의 반환 형식 래핑:
    template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
    yourfunction(args) {// ...
}
// The alias template makes it more concise:
    template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
  • 시나리오 2: 기본 인수가 있는 함수 매개 변수 추가:
    template <your_stuff>
your_return_type_if_present
    yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
  • 시나리오 3: 기본 인수가 있는 템플릿 매개 변수 추가:
    template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
  • 시나리오 4: 함수에 템플릿이 아닌 인수가 있는 경우 해당 인수의 형식을 래핑할 수 있음:
    template <typename T>
void your_function(const T& t,
    enable_if_t<is_something<T>::value, const string&>
s) {// ...
}

생성자 및 변환 연산자에는 반환 형식이 없으므로 시나리오 1의 경우 생성자 및 변환 연산자를 사용하지 않습니다.

시나리오 2는 매개 변수를 명명하지 않은 채 그대로 둡니다. ::type Dummy = BAR이라고 할 수 있지만 이름 Dummy는 상관이 없으므로 매개 변수에 이름을 지정하면 "참조되지 않은 매개 변수" 경고가 트리거될 수 있습니다. FOO 함수 매개 변수 형식과 BAR 기본 인수를 선택해야 합니다. int0이라고 할 수 있지만 코드 사용자가 무시할 수 있는 추가 정수를 실수로 전달할 수 있습니다. 대신 void **0 또는 nullptr을 사용하는 것이 좋습니다. 이는 void **로 변환할 수 있는 것이 거의 없기 때문입니다.

template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

시나리오 2 역시 일반 생성자에 대해서 작동합니다. 그러나 변환 연산자는 추가 매개 변수를 사용할 수 없으므로 이 시나리오는 변환 연산자에 대해서는 작동하지 않습니다. 추가 매개 변수를 추가하면 함수 매개 변수 팩이 추론되지 않는 컨텍스트가 되므로 enable_if생성자에 대해서도 작동하지 variadic 않습니다.

시나리오 3은 이름 Dummy를 사용하지만 이는 선택 사항입니다. "typename = typename"만 작동합니다. 이상하다는 생각이 들면 "dummy" 이름을 사용할 수도 있지만 함수 정의에 사용되었을 수 있는 이름은 사용하지 마세요. enable_if에 형식을 지정하지 않으면 기본적으로 void 형식이 됩니다. 이는 Dummy가 무엇인지 신경을 쓰지 않으므로 완벽하게 합리적입니다. 변환 연산자 및 variadic 생성자를 비롯한 모든 항목에 대해 작동합니다.

시나리오 4는 반환 형식이 없는 생성자에서 작동하므로 시나리오 1의 래핑 제한 문제를 해결합니다. 그러나 시나리오 4는 템플릿이 아닌 함수 인수로 제한됩니다. 이러한 인수는 사용할 수 없는 경우도 있습니다. 템플릿 함수 인수에 시나리오 4를 사용하면 시나리오 4에 대해 템플릿 인수 추론이 수행되지 않습니다.

enable_if는 강력하지만 잘못 사용되면 위험하기도 합니다. 이 클래스의 용도는 오버로드 확인 전 후보를 없애는 것이므로 이 클래스를 잘못 사용하면 그 영향은 매우 혼란스러울 수 있습니다. 몇 가지 권장 사항입니다.

  • 컴파일 시간에 구현 중에서 선택하는 데는 사용하지 enable_if 마세요. enable_if 하나를 CONDITION에 대해 작성하고 다른 하나를 !CONDITION에 대해 작성하지 마세요. 대신 태그 디스패치 패턴을 사용합니다. 예를 들어 지정된 반복기의 강도에 따라 구현을 선택하는 알고리즘을 사용합니다.

  • 요구 사항을 적용하는 데 사용하지 enable_if 마세요. 템플릿 매개 변수의 유효성을 검사하고 유효성 검사에 실패하는 경우 다른 구현 static_assert을 선택하는 대신 오류가 발생합니다.

  • 사용하지 않으면 좋은 코드를 모호하게 만드는 오버로드 집합이 있는 경우에는 enable_if를 사용합니다. 가장 일반적으로 이러한 경우는 생성자를 암시적으로 변환할 때 발생합니다.

예시

이 예제에서는 C++ 표준 라이브러리 템플릿 함수 std::make_pair() 가 어떻게 활용 enable_if되는지 설명합니다.

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

이 예제에서 make_pair("foo", "bar")pair<const char *, const char *>을 반환합니다. 오버로드 확인은 사용자가 원하는 func()를 확인해야 합니다. pair<A, B>에는 pair<X, Y>에서 암시적으로 변환된 생성자가 있습니다. 이는 새로운 기능이 아니라 C++98에 있던 기능입니다. 그러나 C++98/03에서는 pair<int, int>(const pair<const char *, const char *>&)인 경우라도 암시적 변환 생성자의 서명이 항상 존재했습니다. const char *int로 암시적으로 변환할 수 없기 때문에 오버로드 해결은 생성자를 인스턴스화하려는 시도가 끔찍하게 전개된다는 점을 개의치 않고 함수 정의를 인스턴스화하기 전에 서명만 검토합니다. 따라서 서명은 pair<const char *, const char *>pair<int, int>pair<string, string> 둘 다로 변환하기 위해 존재하므로 예제 코드가 모호합니다.

C++11에서는 enable_if를 사용하여 const X&A로 암시적으로 변환 가능하고 const Y&가 로 암시적으로 B로 변환 가능한 경우에만pair<A, B>(const pair<X, Y>&)가 존재하도록 보장하여 이러한 모호성을 해결했습니다. 이렇게 하면 오버로드 확인에서 변환할 수 없고 필요한 pair<string, string> 오버로드가 실행 가능한지 확인할 pair<const char *, const char *>pair<int, int> 있습니다.

요구 사항

헤더<type_traits>:

네임스페이스:std

참고 항목

<type_traits>