【问题标题】:Distribute template wrapper across parameter pack跨参数包分发模板包装器
【发布时间】:2020-04-27 01:53:27
【问题描述】:

我有几个模板类型,Egg<T>Chick<T>

template<typename T>
struct Egg{};

template<typename T>
struct Chick{};

小鸡包含在LoudNest&lt;Chicks...&gt; 类中,鸡蛋包含在QuietNest&lt;Eggs...&gt; 中:

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

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

  // more here.
};

我想在QuietNest&lt;Eggs...&gt; 上有一个hatch 方法来产生一个LoudNest。对于 QuietNest 中的每个 Egg&lt;T&gt;,LoudNest 应该有一个 Chick&lt;T&gt;。我有一个函数QuietNest&lt;Eggs...&gt;::hatch_impl,它可以创建一个std::tuple&lt;Chicks...&gt;,其中Chicks 都有正确的类型参数。也就是说,QuietNest&lt;Egg&lt;double&gt;, Egg&lt;string&gt;, Egg&lt;char&gt;&gt;::hatch_impl 将返回 std::tuple&lt;Chick&lt;double&gt;, Chick&lt;string&gt;, Chick&lt;char&gt;&gt;。我被困在试图将其包装在 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&lt;T&gt; 转换为 Chick&lt;T&gt; 开始,我有:

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&lt;T&gt; (Egg&lt;T&gt;)、ActualParameter&lt;T&gt; (Chick&lt;T&gt;) 等。但是,我想避免使用“参数”这个词在示例代码中,因为我们已经在不同的意义上讨论了参数包。

这篇文章的代码:https://godbolt.org/z/XBIEhm

【问题讨论】:

    标签: c++ templates c++14 variadic-templates


    【解决方案1】:

    我能够通过一些更改来修复您的示例:https://godbolt.org/z/3VW68f

    1. LoudNest 中添加一个推导指南,以缓解在LoudNest{hatchlings} 中推导Chicks... 类型的问题(这可能不是唯一的解决方案,但看起来很干净,因为它不需要复杂的舱口()实施):

      template<typename... Chicks>
      LoudNest(const std::tuple<Chicks...>& chicks) -> LoudNest<Chicks...>;
      
    2. (添加hatchOne,它出现在您的问题中,但不是您共享的godbolt 链接)

    3. 摆脱hatch_impl,而只在包扩展期间调用hatchOne

      auto hatchlings = std::apply(
        [&](auto... args) { return std::make_tuple(hatchOne(args)...); },
        eggs);
      
    4. 使用特化将 Egg 参数的内部T 类型推导出为AsLoudNest1

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-07
      • 2016-01-30
      • 2015-09-20
      • 1970-01-01
      • 2020-07-31
      • 2023-03-16
      • 2020-10-16
      • 1970-01-01
      相关资源
      最近更新 更多