模板类的树,其中 chilldren 是 std::array of std::unique_ptr

Posted

技术标签:

【中文标题】模板类的树,其中 chilldren 是 std::array of std::unique_ptr【英文标题】:Tree of template class where chilldren are std::array of std::unique_ptr 【发布时间】:2015-04-02 13:09:59 【问题描述】:

我的情况如下:

template<size_t Level>
class Node

public:

    Node()
    
        for (size_t i = 0; i < children.size(); i++)
            children[i].reset(new Node<Level - 1>());
    

    array<unique_ptr<Node<Level - 1>>, 4> children;
;


template<>
class Node<0>

;

...

Node<2> root;

我需要创建一个节点树,其中每个节点,但最后一个(级别 = 0),有 4 个子节点。我想将孩子存储在std:arraystd::unique_ptr 中。

我的解决方案是正确的还是我做错了什么?有更聪明的方法来实现这个结果?

【问题讨论】:

好像可以直接使用std::array&lt;Node&lt;Level - 1&gt;, 4&gt; @Jarod42 仅在理论上,如果不使用指针,我无法拥有太深的层次。 如果这确实是您想要实现的目标,那没关系。如果您打算对这 4 个子项中的任何一个使用编译时(而不是运行时)索引,则可以考虑将 std::array&lt;Node,4&gt; 更改为 std::tuple&lt;Node,Node,Node,Node&gt; @Ethouris 看来 OP 可能会将其超出通常适合自动存储的范围,因此使用 std::unique_ptr 的原因。 std::array&lt;Node,4&gt; 在评论中;不是问题。例如,没有指针的 10 层深的树将消耗大约 27MB 的自动存储空间,这比大多数预留的实现要大得多。 @Ethouris 同质std::tuplestd::array 相比几乎没有优势,std::array 也可以与std::get 等一起使用。 【参考方案1】:

我看不出这里有模板的价值。树是固定的,大小有限,你可以把它做成一个值类型。

例如一棵 4 级树,其叶节点包含 V 类型的值:

namespace Tree 

    template <typename V>
    struct Root 

        struct Tier1 
            struct Tier2 
            struct Tier3 
            struct Leaf  V value; ;

            std::array<Leaf, 4> children;
            ;
            std::array<Tier3, 4> children;
            ;
            std::array<Tier2, 4> children;
        ;

        std::array<Tier1, 4> children;
    ;

当然,如果节点可以在所有级别都是可选的,请使用boost::optional&lt;&gt;

std::array<optional<Leaf>, 4> children; ;
std::array<optional<Tier3>, 4> children; ;
std::array<optional<Tier2>, 4> children; ;
std::array<optional<Tier1>, 4> children;

如果你没有 boost,你可以在那里使用 unique_ptr 作为一个穷人的可选项(但这对内存布局有很大影响)。

再来一次,使用 feeling 模板:

你可以用模板表达同样的意思:

Live On Coliru

#include <array>
#include <boost/optional.hpp>

namespace Tree 

    using boost::optional;

    template <typename LeafValue, int level> struct Node;

    template <typename LeafValue> struct Node<LeafValue, 3> 
        LeafValue value;
    ;

    template <typename LeafValue, int level> struct Node 
        std::array<optional<Node<LeafValue, level+1> >, 4> children;
    ;

    template <typename LeafValue> using Root = Node<LeafValue, 0>;



int main() 

    Tree::Root<int> a = 
    ;

【讨论】:

如果编译器在链接器中具有良好的 ICF,代码大小只会在存在实际差异的情况下增加。不可否认,大多数编译器都缺乏 ICF 支持(相同的 comdat 折叠),除了 MSVC 和 gcc 的 Gold 链接器(最后我检查过)。 @Yakk 我错过了关于代码/二进制大小的评论吗? 我想我在你的回答中读到了一些关于“复制方法”之类的东西。我在编辑历史中看不到它,所以也许我产生了幻觉。 @Yakk 我只是对模板的模板持怀疑态度。但是请注意我是如何为LeafValue 添加类型参数的。因为那个对我来说确实有意义

以上是关于模板类的树,其中 chilldren 是 std::array of std::unique_ptr的主要内容,如果未能解决你的问题,请参考以下文章

指向模板类的指针

在自身内部创建模板类的 std 容器

C++ 一个包含多种类型模板类的 std::vector

如何将 std::unordered_map 部分专门化为我的模板类的成员?

派生类的模板类作为函数的参数 - 危险?

STL源码学习std::list类的类型别名分析