【问题标题】:Recursive function acting on variadic class template parameters作用于可变类模板参数的递归函数
【发布时间】:2019-11-10 05:55:18
【问题描述】:

所以我有以下(减少的)课程:

template <typename A, typename... B>
struct ComponentTupleAccessor: 
    public ComponentArray<A>, 
    public ComponentTupleAccessor<B...> 
{
    ComponentTupleAccessor(const uint32_t capacity): 
        ComponentArray<A>(capacity), 
        ComponentTupleAccessor<B...>(capacity) 
    {}
};
template <typename A>
struct ComponentTupleAccessor<A>: 
    public ComponentArray<A> 
{
    ComponentTupleAccessor<A>(const uint32_t capacity): 
        ComponentArray<A>(capacity) 
    {}
};
template <typename A, typename ...B>
class ComponentTuple {
    ComponentTupleAccessor<A, B...> m_Components;
    uint32_t m_Capacity;

public:
    ComponentTuple(const RB32u capacity): 
        m_Capacity{capacity}, 
        m_Components(capacity) 
    {}

    template <typename S, typename ...T>
    void pop_back() {
        m_Components.Component<S>::pop_back();
        pop_back<T...>();
    }

    template <typename S>
    void pop_back() {
        m_Components.Component<S>::pop_back();
    }

    void pop_back() {
        pop_back<A, B...>();
    }
};

ComponentArray 类基本上是一个包含一组特定类型组件的向量的包装器。

ComponentBlockTupleAccessor 类或多或少模拟了 std::tuple 的缩减版本,其中可以使用可变参数模板将任意数量的 ComponentArray 唯一类型继承到 ComponentTuple 类中。

ComponentTuple 中的pop_back 函数旨在递归地pop_back 一个元素从每个ComponentArrays 中分离出来。

ComponentTuple 类之外,我希望能够简单地调用compTupleInstance.pop_back() 之类的东西,并且所有ComponentArray 都应该删除它们的最后一个元素。

我得到一个编译错误“重载‘pop_back()’的调用不明确”pop_back();

我似乎无法找出 A、B(包)、S 和 T(包)模板参数的组合来提供我需要的功能。我在这里错过了什么?

编辑:这是一个简单的使用场景:

// ComponentTuple contains an int and a float ComponentArray with capacity 8.
ComponentTuple<int, float> dut(8);

// Push a set of new components to the ComponentArrays.
// This function has a similar structure to that of pop_back.
dut.push_back({8}, {3.141f});
// Another one
dut.push_back({4}, {2.718f});

// Remove the last element from all of the ComponentArrays.
dut.pop_back();

ComponentTuple 模板参数总是唯一的类型,并且总是大于一。

【问题讨论】:

  • 如果 T 参数包为空,则有 2 个相同的 push_back()
  • 我认为问题在于您的 pop_back 函数不接受任何参数;这意味着它们最终具有相同的签名。也许您可以详细说明您的目标是什么功能。添加一些代码来显示您打算如何调用这些方法?
  • @Willem 是的,你是对的。我有一个类似结构的 push_back 函数,它没有任何问题,因为函数签名总是明确的。我如何在没有至少一个非可变参数的情况下维护递归模板功能?

标签: c++ variadic-templates recursive-datastructures recursive-templates


【解决方案1】:

问题的副本:

    template <typename S, typename ...T>  // T can be empty
    void pop_back() {
        m_Components.Component<S>::pop_back();
        pop_back<T...>();
    }

    template <typename S>
    void pop_back() {
        m_Components.Component<S>::pop_back();
    }

如果我调用pop_back&lt;A&gt;() 我有S = A。但是,我是用T 为空调用第一种方法,还是调用第二种方法?

【讨论】:

    【解决方案2】:

    核心问题:当只有一个模板参数(包可以为空)时,template &lt;typename S, typename ... T&gt;template &lt;typename S&gt; 看起来对编译器同样适用。它无法决定使用哪个重载。

    解决方法:可以使用折叠表达式(c++17或以上)。

    void pop_back() {
        (m_Components.ComponentArray<A>::pop_back(), ... , m_Components.ComponentArray<B>::pop_back());
    }
    

    ...如果像这样使用,代码也会中断(即使使用上面的折叠表达式): ComponentTuple&lt;int, int, double&gt;(不明确的基类)。

    【讨论】:

      【解决方案3】:

      感谢您的帮助,&lt;typename S&gt;&lt;typename S, typename... T&gt; 之间的歧义似乎很明显,因为您已经指出了这一点。由于这个原因,似乎无法按照我尝试的方式进行操作。

      我最终使用if constexpr 来测试递归是否是最后一个类型,因此我可以在该点终止递归。我更喜欢这一点,因为在非模板化pop_back 中,没有提供未用于声明类的模板参数的风险。例如

      ComponentTuple<int, float> dut(8);
      
      push_back<Banana, char>; // Not int, float so should cause compile error.
      

      使用constexpr方法,我可以将pop_back函数私有化(见下面的pop_back_),并且只暴露无参数版本,这样就只能以正确的方式调用该方法:

      template <typename A, typename ...B>
      class ComponentTuple {
          ComponentTupleAccessor<A, B...> m_Components;
          uint32_t m_Capacity;
      
          template <typename S, typename... T>
          void pop_back_() {
              m_Components.ComponentBlock<S>::pop_back();
      
              if constexpr (sizeof...(T) > 0) {
                  pop_back_<T...>();
              }
          }
      
      public:
          ComponentTuple(const RB32u capacity): 
              m_Capacity{capacity}, 
              m_Components(capacity) 
          {}
      
          void pop_back() {
              pop_back_<A, B...>();
          }
      };
      

      显然,我需要确保至少使用 c++17 才能使用 if constexpr

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-04
        • 2012-05-17
        • 1970-01-01
        • 2017-02-23
        • 1970-01-01
        • 2018-03-31
        相关资源
        最近更新 更多