Понимание шаблонов псевдонимов

Я спросил вопрос это имеет несколько ссылок на код:

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

Я считаю, что я вообще неправильно понимаю шаблоны псевдонимов:

Почему бы вам просто не оценить какой-либо параметр шаблона, который вы передаете в шаблон псевдонима в enable_if_t или же conditional_t заявление?

Код выше только о выполнении enable_if_t на несколько параметров шаблона одновременно?

Во-вторых, я считаю, что у меня есть конкретное недопонимание роли void_t, Этот комментарий утверждает, что стандарт C ++ 17 определяет void_t, Вот что я не получаю:

не void_t просто произвольное имя? Если я все еще должен определить template <typename...> using void_t = void; везде, где я планирую использовать void_t какой смысл стандартизировать произвольное имя?

4

Решение

Я не думаю, что показанный пример действительно показывает, что void_t хорошо, так как он показывает только один вариант использования, но когда вы смотрите на

template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };

это не так сильно отличается от

template<typename T>
struct has_to_string<T,
decltype(std::to_string(std::declval<T>()), void())
>
: std::true_type { };

И для этого утверждения:

Предыдущая версия намного легче читать и void_t не требует decltype работать.

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

Один пример, где void_t Более полезным является тот из предложения:

// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };

// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }

Как видите, даже основной шаблон использует void_t повысить удобочитаемость, так как теперь она соответствует специализации. Это не обязательно, но мне это нравится. Настоящая сила приходит, когда вы думаете об альтернативах. Без void_t, специализация теперь сложнее:

template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }

не будет работать как T::type называет тип, а не выражение. Поэтому вам нужно

template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }

Все выражение становится длиннее, хитрее и может пострадать от крайних случаев, которые вы забыли обработать. Это где void_t действительно помогает, другие применения — это лишь небольшое улучшение, и они увеличивают согласованность.

3

Другие решения

В примере Барри из вашего связанного вопроса:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };

void_t просто используется для перевода типа, выведенного decltype в void так что он соответствует аргумент по умолчанию для первичный определение шаблона. О СФИНАЕ все заботятся decltype выражение. Вы можете так же легко сделать следующее:

//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())

Предыдущая версия намного легче читать и void_t не требует decltype работать.

Если void_t доступен в вашей реализации, вам не нужно переопределять его. Когда он будет стандартизирован, он будет доступен, как и любой другой шаблон псевдонима в стандарте.

Подумайте об этом так: если T является int, который имеет действительный std::to_string перегрузка, удержание будет выглядеть так:

has_to_string<int> -> has_to_string<int,void> из-за аргумента по умолчанию. Итак, давайте посмотрим на специализации has_to_string с этими аргументами.

template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };

Хорошо, это частичная специализация для некоторых T и некоторый зависимый тип. Давайте разработаем этот тип:

void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void

Теперь наша специализация выглядит так:

template<>
struct has_to_string<int,void>
: std::true_type { };

Это соответствует нашему воплощению has_string<int,void>, так has_to_string<int> наследуется от std::true_type,

Теперь подумай, когда T является struct Foo{};, Опять же, давайте разработаем этот зависимый тип:

void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization

С этой специализацией мы отказались от основного шаблона:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

Так has_to_string<Foo> наследуется от std::false_type,

8

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector