Как работает " void t



я смотрел выступление Уолтера Брауна на Cppcon14 о современном программировании шаблонов (Часть I,Часть II), где он представил свою void_t техника SFINAE.



пример:

Учитывая простой шаблон переменной, который оценивается в void если все аргументы шаблона формата:



template< class ... > using void_t = void;


и следующий признак, который проверяет наличие переменной-члена с именем :



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 ) > >





    • 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 ) плохо сформирован и молча терпит неудачу (sfinae)


    • has_member< B , expression-sfinae > так что этот шаблон отбрасывается



  • компилятор обнаруживает has_member< B , class = void > С void в качестве аргумента по умолчанию


  • has_member< B > значение false_type


http://ideone.com/HCTlBb



вопросы:

1. Правильно ли я это понимаю?

2. Уолтер Браун утверждает, что аргумент по умолчанию должен быть точно такого же типа, как и в void_t чтобы он работал. Почему? (Я не понимаю, почему эти типы должны совпадать, не только любой тип по умолчанию работает?)

816   2  

2 ответов:

когда вы пишите 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
{ };

компилятор пытается сопоставить аргументы шаблона A, void с шаблонами, определенными в частичной специализации:T и void_t<..> по одиночке. Во-первых, выполняется вывод аргумента шаблона. Частичная специализация выше по-прежнему является шаблоном с шаблоном-параметрами, которые должны быть "заполнены" аргументами.

первая модель,T, позволяет компилятору вывести шаблон-параметр T. Это тривиальный вывод, но рассмотрим такой шаблон, как T const&, где мы еще могли вывести T. По шаблону T и аргумент шаблона A, то вывести T будет A.

во втором паттерне void_t< decltype( T::member ) > шаблон-параметр T появляется в контексте, где он не может быть выведен из любого аргумента шаблона. Для этого есть две причины:

  • выражение внутри decltype явно исключается из вычета аргумента шаблона. Я думаю, это потому, что он может быть сколь угодно сложной.

  • даже если мы использовали шаблон без decltype как void_t< T >, тогда вычитание T происходит на разрешенном шаблоне псевдонима. То есть мы разрешаем шаблон псевдонима и затем пытаемся вывести тип T из полученного шаблона. Однако результирующий шаблон void, который не зависит от T и поэтому не позволяет нам найти конкретный тип для T. Это похоже на математическую задачу о попытке инвертировать постоянную функцию (в математическом смысле этих терминов).

шаблон вывод аргумента завершен(*), теперь вывести аргументы шаблона подставляются. Это создает специализацию, которая выглядит следующим образом:

template<>
struct has_member< A, void_t< decltype( A::member ) > > : true_type
{ };

тип void_t< decltype( A::member ) > > теперь можно оценить. Он хорошо формируется после замены, следовательно, нет Замещение Отказ происходит. Получаем:

template<>
struct has_member<A, void> : true_type
{ };

теперь мы можем сравнить список параметров шаблона этой специализации с аргументами шаблона, предоставленными в оригинал has_member<A>::value. Оба типа точно совпадают, поэтому выбрана эта частичная специализация.

С другой стороны, когда мы определяем шаблон:

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
{ };

мы в конечном итоге с той же специализации:

template<>
struct has_member<A, void> : true_type
{ };

но наш список аргументов шаблона для has_member<A>::value сейчас <A, int>. Аргументы не совпадают с параметрами специализации,и основной шаблон выбирается в качестве резервного.


(*) Стандартный, IMHO путает, включает в себя процесс подстановки и сопоставления явно указанных аргументов шаблона в вычет аргумента шаблона

// specialized as has_member< T , void > or discarded (sfinae)
template<class T>
struct has_member<T , void_t<decltype(T::member)>> : true_type
{ };

что выше специализация существует только тогда, когда она хорошо сформирована, так когда decltype( T::member ) является допустимым и не неоднозначным. специализация-это так, для has_member<T , void> как указано в комментарии.

когда вы пишите has_member<A>, это has_member<A, void> из-за аргумента шаблона по умолчанию.

и у нас есть специализация на has_member<A, void> (поэтому наследовать от true_type) но у нас нет специализации has_member<B, void> (поэтому мы используем значение по умолчанию : наследовать от false_type)

Comments

    Ничего не найдено.