#c #templates #recursion #variadic-templates #template-meta-programming
#c #шаблоны #рекурсия #переменные-шаблоны #шаблон-метапрограммирование
Вопрос:
Я пытаюсь написать метафункцию C , которая возвращает мне первый непустой подтип для предоставленных параметров шаблона.
Например:
struct I { using subtype = int; };
struct D { using subtype = double; };
struct E { using subtype = empty ; };
Я пытаюсь достичь:
static_assert(std::is_same<int, first_non_empty_subtype<E,E,I>>::value, "the first non-empty subtype should be 'int'");
static_assert(std::is_same<double, first_non_empty_subtype<E,D,I>>::value, "the first non-empty subtype should be 'double'");
static_assert(std::is_same<empty, first_non_empty_subtype<E,E,E>>::value, "since all subtypes are empty, the result is empty");
Мои первоначальные мысли заключаются в использовании std::conditional_t
с рекурсией шаблона:
template <typename T, typename ...Ts>
using first_non_empty_subtype = std::conditional_t<
!std::is_empty<typename T::subtype>::value,
typename T::subtype,
first_non_empty_subtype<Ts...>>::type
Однако я не совсем знаком с реализацией рекурсии шаблона для псевдонимов типов.
Может кто-нибудь помочь мне указать правильное направление для решения этой проблемы?
Спасибо!
Комментарии:
1. Чтобы рекурсия работала, вам нужно что-то, что прерывает рекурсию. В вашем примере такой вещи нет, и AFAIK это невозможно сделать с помощью псевдонимов шаблонов, вы никак не можете их специализировать. Вы можете создать рекурсивную структуру, которая содержит только
using subtype = ...
и использовать это. Если вы хотите, вы можете создать псевдоним шаблона, который указывает на рекурсивную структуру для упрощения использования.2. Спасибо, что указали на это! Я намеренно опустил это, потому что не был уверен, как это будет реализовано. Спасибо за объяснение того, что псевдонимы шаблонов не могут быть специализированными — @max66 показывает решение, использующее рекурсивную структуру.
Ответ №1:
Я предлагаю что-то вроде следующего
// ground case: no more types, so empty
template <typename ...>
struct fnes_helper
{ using type = empty; };
// the first type is T and isn't empy; so T
template <typename T, typename ... Ts>
struct fnes_helper<T, Ts...>
{ using type = T; };
// the first type is empty; so recursion
template <typename ... Ts>
struct fnes_helper<empty, Ts...> : public fnes_helper<Ts...>
{ };
template <typename ... Ts>
using first_non_empty_subtype
= typename fnes_helper<typename Ts::subtype...>::type;
Обратите внимание, что fnes_helper
более специализированная версия — это та, в которой empty
тип находится на первой позиции, поэтому в этом случае используется версия.
Далее следует другая специализация, с универсальным T
типом на первой позиции, и, наконец, у нас есть основная версия, которая выбирается в других случаях, поэтому список типов пуст.
Также не забудьте добавить {}
или ::value
после std::is_same
в static_assert()
тестах
static_assert(std::is_same<int, first_non_empty_subtype<E,E,I>>{},
"the first non-empty subtype should be 'int'");
static_assert(std::is_same<double, first_non_empty_subtype<E,D,I>>{},
"the first non-empty subtype should be 'double'");
static_assert(std::is_same<empty, first_non_empty_subtype<E,E,E>>{},
"since all subtypes are empty, the result is empty");
Комментарии:
1. Круто, спасибо! Это было то, что я искал. Спасибо за быстрый ответ и за то, что нашли время объяснить, как работают решения.