Oglądałem wykład Waltera Browna na Cppcon14 o nowoczesnym programowaniu szablonów ( część I , część II ), gdzie przedstawił swoją void_t
technikę SFINAE.
Przykład:
biorąc pod uwagę prosty szablon zmiennej, który ocenia, void
czy wszystkie argumenty szablonu są poprawnie sformułowane:
template< class ... > using void_t = void;
oraz następującą cechę, która sprawdza istnienie zmiennej składowej o nazwie 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
{ };
Próbowałem zrozumieć, dlaczego i jak to działa. Dlatego mały przykład:
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
istniejedecltype( A::member )
jest dobrze uformowanyvoid_t<>
jest ważny i jest oceniany dovoid
has_member< A , void >
i dlatego wybiera specjalistyczny szablonhas_member< T , void >
i ocenia dotrue_type
2. has_member< B >
has_member< B , void_t< decltype( B::member ) > >
B::member
nie istniejedecltype( B::member )
jest źle ukształtowany i cicho zawodzi (sfinae)has_member< B , expression-sfinae >
więc ten szablon jest odrzucany
- kompilator znajdzie
has_member< B , class = void >
argument void jako domyślny has_member< B >
ocenia dofalse_type
Pytania:
1. Czy moje rozumienie tego jest prawidłowe?
2. Walter Brown stwierdza, że domyślny argument musi być dokładnie tego samego typu, co używany w programie void_t
, aby działał. Dlaczego? (Nie rozumiem, dlaczego te typy muszą pasować, czy żaden domyślny typ nie spełnia tego zadania?)
has_member< T , class = void >
wpadania w zwłokę void
. Zakładając, że ta cecha będzie używana tylko z 1 argumentem szablonu w dowolnym momencie, to domyślny argument może być dowolnego typu?
template <class, class = void>
na template <class, class = void_t<>>
. Więc teraz możemy robić, co chcemy, z void_t
implementacją szablonu aliasu :)
has_member<A,int>::value
. Wtedy częściowa specjalizacja, którejhas_member<A,void>
wynikiem jest ocena, nie może być zgodna. Dlatego musi to byćhas_member<A,void>::value
lub, w przypadku cukru składniowego, domyślny argument typuvoid
.