Мета-программирование шаблона: как объединить пакеты параметров в новый пакет параметров - PullRequest
2 голосов
/ 18 февраля 2020

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

  1. Можно ли «упаковать» типы, из которых состоит кортеж обратно в пакет параметров? (tuple -> typename ... contains_types)
  2. Возможно ли объединить параметр. pack с именем типа (например, использовать «Pack1 ..., Pack2 ...», указывающий один пакет параметров для структуры?
#include <cstdint>
#include <tuple>

template <typename... tpl> struct Helper {
  template <std::size_t rem, typename curr, typename... rest> struct take {
    using type = Helper<(tpl..., curr)>::take<rem-1, rest...>::type; // here, I'm trying (2.)
  };

  template <typename curr, typename... rest> struct take<0, curr, rest...> {
    using type = std::tuple<tpl...>;
  };
};

template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;

int main() {
  take_t<2, int, int, int> k = std::make_tuple(1, 2);
}

edit Ошибка помощника строки со следующим сообщением:

/home/juli/test.cc:6:18: error: need ‘typename’ before ‘Helper<tpl ..., curr>::take’ because ‘Helper<tpl ..., curr>’ is a dependent scope
    6 |     using type = Helper<tpl..., curr>::take<rem-1, rest...>::type;

и когда я предоставляю имя типа

/home/juli/test.cc:6:53: error: expected ‘;’ before ‘<’ token
    6 |     using type = typename Helper<tpl..., curr>::take<rem-1, rest...>::type;

edit2 Я добился этого с помощью [вспомогательных функций] (https://gist.github.com/juliusHuelsmann/669f537aeb5e7105386d510d186b24e1 ), но они не работают с не примитивными типами, когда конструктор не является constexpr, поэтому я не могу использовать его в моем случае использования, и мне любопытно узнать, как этого добиться и почему мой подход не удался.

1 Ответ

0 голосов
/ 18 февраля 2020

Ответ на Вопрос 2 ) Да, возможно, как подсказал @ user253751, мой первоначальный подход (Helper<tpl..., curr>) правильный, хотя я допустил другую ошибку, которая вызвала вышеупомянутую ошибку в том, что Строка, пропуская «шаблон» после используемого типа. Приведенный ниже код показывает исправление и работает нормально:

#include <cstdint>
#include <tuple>

template <typename... tpl> struct Helper {
  template <std::size_t rem, typename curr, typename... rest> struct take {
    using tp = Helper<tpl..., curr>;
    using type = tp::template take<rem-1, rest...>::type;
  };

  template <typename curr, typename... rest> struct take<0, curr, rest...> {
    using type = std::tuple<tpl...>;
  };
};

template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;

int main() {
  take_t<2, int, int, int>::type k = std::make_tuple(1, 2);
}

Ответ на Вопрос 1 дан трюком, выполненным здесь , см. Полный пример здесь . Это возможно, например, с помощью следующей конструкции (адаптированной на примере «на лету» и непроверенной):

template <class tuple> struct foo;

template <class ... args> struct foo<std::tuple<args...> {
    // implementation here!
};

, которая очень аккуратна и может быть использована также для integer_sequence (которую я пробовал и не удалось делать до сих пор).

полная реализация того, чего я хотел достичь, гораздо более ясная, чем мой первоначальный подход выше, можно найти здесь :

template <size_t s, typename is, typename ...args> struct thlp;

template <size_t s, size_t... i, class... args> struct thlp<s, std::index_sequence<i...>, args...> {
  static_assert(s <= sizeof...(args), "Requested new tuple size exceeds old one.");
  using type = std::tuple<std::tuple_element_t<i, std::tuple<args...>>...>;
};

template <size_t s, typename... args>
using conditional_tuple_t = thlp<s, decltype(std::make_index_sequence<s>()), args...>::type;
...