programing

Eric Niebler의 std :: is_function 구현은 어떻게 작동합니까?

sourcetip 2021. 1. 15. 20:27
반응형

Eric Niebler의 std :: is_function 구현은 어떻게 작동합니까?


지난주 Eric Niebler traits 클래스에 대한 매우 간결한 구현을 트윗 했습니다 std::is_function.

#include <type_traits>

template<int I> struct priority_tag : priority_tag<I - 1> {};
template<> struct priority_tag<0> {};

// Function types here:
template<typename T>
char(&is_function_impl_(priority_tag<0>))[1];

// Array types here:
template<typename T, typename = decltype((*(T*)0)[0])>
char(&is_function_impl_(priority_tag<1>))[2];

// Anything that can be returned from a function here (including
// void and reference types):
template<typename T, typename = T(*)()>
char(&is_function_impl_(priority_tag<2>))[3];

// Classes and unions (including abstract types) here:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

template <typename T>
struct is_function
    : std::integral_constant<bool, sizeof(is_function_impl_<T>(priority_tag<3>{})) == 1>
{};

하지만 어떻게 작동합니까?


일반적인 아이디어

cpprefereence.com샘플 구현 과 같이 유효한 모든 함수 유형을 나열하는 대신 이 구현은 함수 아닌 모든 유형을 나열한 다음 true일치 하는 유형이 없는 경우 에만 확인합니다 .

비 기능 유형 목록은 다음으로 구성됩니다 (아래에서 위로).

  • 클래스 및 공용체 (추상 유형 포함)
  • 함수에서 반환 할 수있는 모든 것 ( void및 참조 유형 포함)
  • 배열 유형

이러한 비 함수 유형과 일치하지 않는 유형은 함수 유형입니다. 참고 std::is_function명시 같은 함수 호출 연산자 람다 또는 클래스 등 호출 유형 고려 하지 기능을 주도한다.

is_function_impl_

is_function_impl가능한 각 비 기능 유형에 대해 하나의 함수 오버로드를 제공 합니다. 함수 선언은 파싱하기가 약간 어려울 수 있으므로 클래스 및 공용체 사례 의 예를 들어 분석해 보겠습니다 .

template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

이 줄은 is_function_impl_유형의 단일 인수를 사용 priority_tag<3>하고 4 char배열에 대한 참조를 반환 하는 함수 템플릿 선언합니다 . C의 고대 시대부터 관례 적으로 선언 구문은 배열 유형의 존재로 인해 끔찍하게 복잡해졌습니다.

이 함수 템플릿은 두 개의 템플릿 인수를 사용합니다. 첫 번째는 제한되지 T않지만 두 번째는 T유형 의 멤버에 대한 포인터 int입니다. int여기에 있는 부분은별로 중요하지 않습니다. 이것은 T타입의 멤버가없는 에서도 작동 합니다 int. 하지만 T클래스 또는 공용체 유형이 아닌에 대해 구문 오류가 발생한다는 것입니다 . 다른 유형의 경우 함수 템플릿을 인스턴스화하려고하면 대체 실패가 발생합니다.

두 번째 템플릿 인수를 사용하여 각각 유효한 함수 반환 유형 또는 배열 유형에 대해서만 컴파일되는 표현식을 형성 하는 priority_tag<2>priority_tag<1>오버로드에도 유사한 트릭이 사용 T됩니다. priority_tag<0>오버로드 에만 이러한 제한적인 두 번째 템플릿 매개 변수가 없으므로 T.

대체로 우리 is_function_impl_는 입력 인수와 반환 유형에 따라 다른 네 가지 오버로드를에 대해 선언 합니다. 각각은 다른 priority_tag유형을 인수로 취하고 다른 고유 크기의 char 배열에 대한 참조를 리턴합니다.

태그 발송 is_function

인스턴스화 할 때 지금 is_function,이 인스턴스 is_function_impl와 함께 T. 이 함수에 대해 4 개의 서로 다른 과부하를 제공 했으므로 여기서 과부하 해결이 발생해야합니다. 그리고 이러한 모든 오버로드는 함수 템플릿 이므로 SFINAE시작할 기회가 있음을 의미 합니다.

따라서 함수 (및 함수 만)의 경우 가장 일반적인 오버로드를 제외하고 모든 오버로드가 실패합니다 priority_tag<0>. 그렇다면 인스턴스화가 가장 일반적인 과부하 인 경우 항상 해당 과부하로 해결되지 않는 이유는 무엇입니까? 오버로드 된 함수의 입력 인수 때문입니다.

Note that priority_tag is constructed in such a way that priority_tag<N+1> publicly inherits from priority_tag<N>. Now, since is_function_impl is invoked here with priority_tag<3>, that overload is a better match than the others for overload resolution, so it will be tried first. Only if that fails due to a substitution error the next-best match is tried, which is the priority_tag<2> overload. We continue in this way until we either find an overload that can be instantiated or we reach priority_tag<0>, which is not constrained and will always work. Since all of the non-function types are covered by the higher prio overloads, this can only happen for function types.

Evaluating the result

We now inspect the size of the type returned by the call to is_function_impl_ to evaluate the result. Remember that each overload returns a reference to a char array of different size. We can therefore use sizeof to check which overload was selected and only set the result to true if we reached the priority_tag<0> overload.

Known Bugs

Johannes Schaub found a bug in the implementation. An array of incomplete class type will be incorrectly classified as a function. This is because the current detection mechanism for array types does not work with incomplete types.

ReferenceURL : https://stackoverflow.com/questions/43470741/how-does-eric-nieblers-implementation-of-stdis-function-work

반응형