繁体   English   中英

如何使用`std :: array`作为`template模板的模板参数 <typename> class`?

[英]How can I use `std::array` for a template parameter of the form `template<typename> class`?

请考虑以下tree

template<typename T, template<typename> class Tuple>
class tree
{
private:
    T m_value;
    Tuple<tree> m_children;
};

template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;

这没有明确的定义。 std::array<T, N>不是Tuple的合适模板参数。 我假设static_tree的意图是明确的。 我们可以做点什么

template<std::size_t N>
struct helper
{
    template<typename T>
    using type = std::array<T, N>;
};

template<typename T, std::size_t N>
using static_tree = tree<T, helper<N>::template type>;

没有helper类还有其他选择吗?

而不是将辅助函数作为std::array的例外,我建议将其作为规则。 不使用模板模板参数,而是使用元函数类参数。 当所有地方都是类型(避免模板模板和非类型参数)时,模板元编程会轻松得多:

template<typename T, typename TupleMfn>
class tree
{
private:
    using Tuple = TupleMfn::template apply<tree>;

    T m_value;
    Tuple m_children;
};

有:

template <size_t N>
struct array_builder {
    template <class T>
    using apply = std::array<T, N>;
};

template <typename T, size_t N>
using static_tree = tree<T, array_builder<N>>;

这样可以更容易地与其他类型的容器一起使用,因为我们可以为模板模板创建一个包装器,为我们提供一个元函数:

template <template <typename...> class X>
struct as_metafunction {
    template <class... Args>
    using apply = X<Args...>;
}; 

template <typename T>
using vector_tree = tree<T, as_metafunction<std::vector>>;

如果你感觉特别激动,你可以提供一个元编程友好版本的std::array

template <class T, class N>
struct meta_array : std::array<T, N::value> // obviously I am terrible at naming things
{ };

template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;

然后提供tree占位符args以应用:

template <class T, size_t N>
using static_tree = tree<T, meta_array<_, size_t_<N>>>;

template <class T>
using vector_tree = tree<T, std::vector<_>>;

我认为你的问题包含一个与你明确提出的问题不同的基本问题(它在本回答的前一次迭代中让我失望了)。 结合你问题的不同部分,你实际上是在尝试实例化一些树类,它的成员也是同一个类的std::array 这显然是不可能的。 您可能希望树应该包含一些指针 Tuple (智能或其他)。

一种方法是使用你的助手类,但将类修改为

template<typename T, template<typename> class Tuple>
class tree
{
    // Indirection (I'm omitting the question of whether these should be
    //     smart pointers.
    Tuple<tree<T, Tuple> *> m_children;
};

一种不同的方法会使Tuple成为常规模板参数,如下所示:

#include <array>
#include <type_traits>

template<typename T, class Tuple>
class tree                                                                                                                                  
{
private:
    static_assert(
        std::is_same<void *, typename Tuple::value_type>::value, 
        "Tuple must be a container of void *");

private:
    T m_value;
    Tuple m_children;
};

template<typename T, std::size_t N>
using static_tree = tree<T, std::array<void *, N>>;

int main()
{
    static_tree<int, 8> t;
}

一方面,辅助类已被淘汰。 OTOH, Tuplevoid *的容器:实例化器知道这一点,并且类内部需要执行强制转换。 这是一个权衡。 我会坚持你的原始版本(当然有修改建议)。

除了逻辑上,类X不能包含类X的实际实例的多个副本。

如果我们有

struct X {
  std::array<X, 2> data;
};

X的唯一可能大小是无穷大,因为sizeof(X) = 2*sizeof(X) ,并且C ++中的所有类型都有sizeof(X)>=1

C ++不支持无限大类型。

您的第二个问题是类型实例不是模板。

template<typename T, template<typename> class Tuple>
class tree

这需要一个类型T和一个template Tuple 第二个参数不是类型

template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;

在这里,你的第二个参数是一个类型 ,而不是一个模板。

template<std::size_t N>
struct array_of_size {
  template<class T>
  using result=std::array<T,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_size<N>::template result>;

除了上述“无限大小问题”之外,还能解决你的问题。 这里我们将模板array_of_size<N>::result传递给tree

要解决无限大小问题, 必须在数组中存储指针(或等效的东西)。 所以我们得到:

template<std::size_t N>
struct array_of_ups_of_size {
  template<class T>
  using result=std::array<std::unique_ptr<T>,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_ups_of_size<N>::template result>;

现在你的static_tree有N个子节点,每个子节点都是一个类似static_treeunique_ptr

由于析构函数问题,这仍然不起作用。

template<typename T, template<typename> class Tuple>
class tree
{
private:
  T m_value;
  Tuple<tree> m_children;
public:
  ~tree();
};

template<typename T, template<typename> class Tuple>
tree<T,Tuple>::~tree() = default;

我认为以上修复了它,看起来很奇怪。

基本上,当您创建子数组时,树类型不完整。 在销毁时,调用delete。 那时,树必须完整。 通过推迟dtor,我们希望能够处理这个问题。

我不确定模板是否需要这种技术,但它适用于非模板类。

或者您可以按照建议实现模板参数绑定(稍微比您的helper更通用):

template<std::size_t N, template<typename,std::size_t> class T>
struct bind2nd
{
    template<typename F>
    using type = T<F,N>;
};

template<std::size_t N, typename T>
using static_tree = tree<T, bind2nd<N,std::array>::template type>;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM