Распределить оболочку шаблона по пакету параметров - PullRequest
1 голос
/ 27 апреля 2020

У меня есть пара шаблонных типов, Egg<T> и Chick<T>.

template<typename T>
struct Egg{};

template<typename T>
struct Chick{};

Цыплята содержатся в классе LoudNest<Chicks...>, а яйца в QuietNest<Eggs...>:

template <typename... Chicks>
struct LoudNest {
  std::tuple<Chicks...> chicks;
};

template <typename... Eggs>
struct QuietNest {
  std::tuple<Eggs...> eggs;

  // more here.
};

Я хочу иметь метод hatch для QuietNest<Eggs...>, который выдает LoudNest. У LoudNest должно быть Chick<T> для каждого Egg<T> в QuietNest. У меня есть функция QuietNest<Eggs...>::hatch_impl, которая может создать std::tuple<Chicks...>, где все Chick имеют правильные параметры типа. То есть QuietNest<Egg<double>, Egg<string>, Egg<char>>::hatch_impl вернет std::tuple<Chick<double>, Chick<string>, Chick<char>>. Я застреваю, пытаясь обернуть это в конструкторе LoudNest:

template <typename... Eggs>
struct QuietNest {
  std::tuple<Eggs...> eggs;

  auto hatch() const {
    // hatchlings is a std::tuple of chicks templated how I want.
    auto hatchlings = std::apply(
      [&](auto... args) { return hatch_impl(std::make_tuple(), args...); },
      eggs);
    // This line causes an error:
    return LoudNest{hatchlings};
    // error: cannot refer to class template 'LoudNest' without a template argument
  }

  // The rest of this all works, but is included in case you want to poke at it:

  // base case: only one parameter was passed—the tuple of hatched chicks.
  template<typename...Chicks>
  std::tuple<Chicks...> hatch_impl(std::tuple<Chicks...> chicks) {
    return chicks;
  }

  // recursive case: in addition to the tuple of hatched chicks,
  // at least one egg was passed (possibly more in the tail)
  template<typename...Chicks, typename T, typename...Unhatched>
  std::tuple<Chicks..., Chick<T>> hatch_impl(
    std::tuple<Chicks...> chicks,
    const Egg<T>& egg,
    Unhatched... tail
  ) const {
    Chick<T> babyBird = hatchOne(egg);
    return hatch_impl(
      std::tuple_cat(chicks, std::make_tuple(babyBird)),
      tail...);
  }

  template<T>
  Chick<T> hatchOne(Egg<T> egg) { return Chick<T>{}; }
};

Я думаю, мне нужно сделать «конвертер», который принимает пакет параметров яиц и производит LoudNest с цыплятами соответствующих типов. Начиная с преобразования одного Egg<T> в Chick<T>, у меня есть:

template<typename T>
struct AsChick {
  using type = T;
};

template< template <typename> class E, typename T>
struct AsChick<E<T>> {
  static_assert(std::is_same<E<T>, Egg<T>>::value, "Expected AsChick to be used with an Egg<T>");
  using type = Chick<T>;
};

Когда я застреваю, когда я пытаюсь сделать то же самое для пакета параметров:

template<typename... Eggs>
struct AsLoudNest1 {
    using type = LoudNest<
        (AsChick<Eggs>::type)...
        // I want this to expand Eggs to produce
        // AsChick<Eggs0>::type, AsChick<Eggs1>::type, AsChick<Eggs2>::type, ...
        // but it doesn't looks like that's a supported type of expansion
    >;
};
static_assert(std::is_same<
  AsLoudNest1<Egg<int>, Egg<double>>::type,
  LoudNest<Chick<int>, Chick<double>>
>::value, "Expected AsLoudNest1 to convert my Egg<T>s to Chick<T>s");

И попробуйте номер два:

template <
    class E, // does this need to be template<typename> class E?
    typename... Rest>
struct AsLoudNest2 {
    using type = LoudNest<
      // Pretty sure the beginning is right.
      AsChick<E>::type,

      // This line feels wrong, AsLoudNest2<...>::type is a concrete type, not a parameter pack
      AsLoudNest2<Rest...>::type...
    >;
};
// also, feels like I need a base case for AsLoudNest2?

Моя настоящая проблема связана с реализацией интерпретатора, а классы: FormalParameter<T> (Egg<T>), ActualParameter<T> (Chick<T>) и др. c. Однако я хотел бы избежать использования слова «параметр» для в примере кода, поскольку мы уже говорим о пакетах параметров в другом смысле.

код из этого поста: https://godbolt.org/z/XBIEhm

1 Ответ

2 голосов
/ 27 апреля 2020

Я смог исправить ваш пример с несколькими изменениями: https://godbolt.org/z/3VW68f

  1. Добавьте руководство по выводам к LoudNest, чтобы устранить проблему, выводящую Chicks... вводит LoudNest{hatchlings} (это, вероятно, не единственное решение, но кажется чистым, поскольку не требует усложнения реализации hatch ()):

    template<typename... Chicks>
    LoudNest(const std::tuple<Chicks...>& chicks) -> LoudNest<Chicks...>;
    
  2. (Добавьте hatchOne, который присутствовал в вашем вопросе, но не ссылку на Годболт, которой вы поделились)

  3. Избавьтесь от hatch_impl в пользу простого вызова hatchOne во время расширения пакета:

    auto hatchlings = std::apply(
      [&](auto... args) { return std::make_tuple(hatchOne(args)...); },
      eggs);
    
  4. Используйте специализацию для вывода внутренних T типов параметров Egg на AsLoudNest1:

    template<typename... Eggs>
    struct AsLoudNest1 {};
    
    template<typename... Ts>
    struct AsLoudNest1<Egg<Ts>...> {
        using type = LoudNest<Chick<Ts>...>;
    };
    
...