`void_t`는 어떻게 작동합니까
나는 Cppcon14에서 Walter Brown이 SFINAE 기술 을 발표 한 최신 템플릿 프로그래밍 ( Part I , Part II ) 에 대해 이야기 한 것을 보았다 void_t
.
예 : 모든 템플릿 인수가
올바른지 평가하는 간단한 변수 템플릿이 제공됩니다 void
.
template< class ... > using void_t = void;
그리고 member라는 멤버 변수가 있는지 확인하는 다음 특성 :
template< class , class = void >
struct has_member : std::false_type
{ };
// specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };
나는 왜 그리고 어떻게 작동하는지 이해하려고 노력했다. 따라서 작은 예 :
class A {
public:
int member;
};
class B {
};
static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );
1. has_member< A >
has_member< A , void_t< decltype( A::member ) > >
A::member
존재decltype( A::member )
잘 구성되어있다void_t<>
유효하고 평가void
has_member< A , void >
따라서 전문화 된 템플릿을 선택합니다has_member< T , void >
평가true_type
2. has_member< B >
has_member< B , void_t< decltype( B::member ) > >
B::member
존재하지 않는다decltype( B::member )
잘못 형성되었으며 자동으로 실패 (정사각형)has_member< B , expression-sfinae >
이 템플릿은 삭제됩니다
- 컴파일러는
has_member< B , class = void >
void를 기본 인수로 찾습니다. has_member< B >
~에 평가하다false_type
질문 :
1. 이것에 대한 나의 이해가 맞습니까?
2. Walter Brown은 기본 인수가 void_t
작동하기 위해 사용 된 것과 동일한 유형이어야한다고 말합니다 . 왜 그런 겁니까? (이 유형이 일치 해야하는 이유를 모르겠습니다. 기본 유형만이 작동하지 않습니까?)
작성 has_member<A>::value
하면 컴파일러가 이름 has_member
을 찾고 기본 클래스 템플릿, 즉이 선언을 찾습니다 .
template< class , class = void >
struct has_member;
(OP에서는 정의로 작성되었습니다.)
템플릿 인수 목록 <A>
은이 기본 템플릿의 템플릿 매개 변수 목록과 비교됩니다. 기본 템플리트에는 두 개의 매개 변수가 있지만 하나만 제공 했으므로 나머지 매개 변수는 기본적으로 기본 템플리트 인수로 설정됩니다 void
. 마치 당신이 쓴 것처럼입니다 has_member<A, void>::value
.
이제 템플릿 매개 변수 목록이 템플릿의 모든 특수화와 비교 has_member
됩니다. 일치하는 전문화가없는 경우에만 기본 템플릿의 정의가 대체로 사용됩니다. 따라서 부분 전문화가 고려됩니다.
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };
The compiler tries to match the template arguments A, void
with the patterns defined in the partial specialization: T
and void_t<..>
one by one. First, template argument deduction is performed. The partial specialization above is still a template with template-parameters that need to be "filled" by arguments.
The first pattern, T
, allows the compiler to deduce the template-parameter T
. This is a trivial deduction, but consider a pattern like T const&
, where we could still deduce T
. For the pattern T
and the template argument A
, we deduce T
to be A
.
In the second pattern void_t< decltype( T::member ) >
, the template-parameter T
appears in a context where it cannot be deduced from any template argument. There are two reasons for this:
The expression inside
decltype
is explicitly excluded from template argument deduction. I guess this is because it can be arbitrarily complex.Even if we used a pattern without
decltype
likevoid_t< T >
, then the deduction ofT
happens on the resolved alias template. That is, we resolve the alias template and then try to deduce the typeT
from the resulting pattern. The resulting pattern however isvoid
, which is not dependent onT
and therefore does not allow us to find a specific type forT
. This is similar to the mathematical problem of trying to invert a constant function (in the mathematical sense of those terms).
Template argument deduction is finished(*), now the deduced template arguments are substituted. This creates a specialization that looks like this:
template<>
struct has_member< A, void_t< decltype( A::member ) > > : true_type
{ };
The type void_t< decltype( A::member ) > >
can now be evaluated. It is well-formed after substitution, hence, no Substitution Failure occurs. We get:
template<>
struct has_member<A, void> : true_type
{ };
Now, we can compare the template parameter list of this specialization with the template arguments supplied to the original has_member<A>::value
. Both types match exactly, so this partial specialization is chosen.
On the other hand, when we define the template as:
template< class , class = int > // <-- int here instead of void
struct has_member : false_type
{ };
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };
We end up with the same specialization:
template<>
struct has_member<A, void> : true_type
{ };
but our template argument list for has_member<A>::value
now is <A, int>
. The arguments do not match the parameters of the specialization, and the primary template is chosen as a fall-back.
(*) The Standard, IMHO confusingly, includes the substitution process and the matching of explicitly specified template arguments in the template argument deduction process. For example (post-N4296) [temp.class.spec.match]/2:
A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list.
But this does not just mean that all template-parameters of the partial specialization have to be deduced; it also means that substitution must succeed and (as it seems?) the template arguments have to match the (substituted) template parameters of the partial specialization. Note that I'm not completely aware of where the Standard specifies the comparison between the substituted argument list and the supplied argument list.
// specialized as has_member< T , void > or discarded (sfinae)
template<class T>
struct has_member<T , void_t<decltype(T::member)>> : true_type
{ };
That above specialization exists only when it is well formed, so when decltype( T::member )
is valid and not ambiguous. the specialization is so for has_member<T , void>
as state in the comment.
When you write has_member<A>
, it is has_member<A, void>
because of default template argument.
And we have specialization for has_member<A, void>
(so inherit from true_type
) but we don't have specialization for has_member<B, void>
(so we use the default definition : inherit from false_type
)
참고URL : https://stackoverflow.com/questions/27687389/how-does-void-t-work
'Programming' 카테고리의 다른 글
PDO가있는 PHP에서 최종 SQL 매개 변수화 쿼리를 확인하는 방법은 무엇입니까? (0) | 2020.06.23 |
---|---|
브라우저에서 마우스 휠 속도 정상화 (0) | 2020.06.23 |
.net 4에서 async-await 사용 (0) | 2020.06.23 |
기본 인코딩이 ASCII 일 때 파이썬이 유니 코드 문자를 인쇄하는 이유는 무엇입니까? (0) | 2020.06.23 |
앱이 사용할 수있는 최대 RAM 양은 얼마입니까? (0) | 2020.06.23 |