如何初始化嵌套结构的unique_ptr(例如二叉树)

How to initialize unique_ptr of nested structs (e.g. for binary trees)

本文关键字:ptr 二叉树 unique 初始化 嵌套 结构      更新时间:2023-10-16

在现代c++中,通常建议在处理二叉树时使用unique_ptr来明确子树的所有权。例如,编程面试元素建议:

template <typename T>
struct Node {
  T data;
  unique_ptr<Node<T>> left, right;
};

我正在学习c++ 11的特性,我想知道什么是最方便的方法来初始化一个二叉树对应于一个特定的结构。我的用例是为特定的树编写单元测试。例如,我想生成这样的树:

    5
   / 
  3   4
 / 
1   2

下面的代码可以工作,但是非常麻烦:

// first attempt: temporary variables & high syntactic noise
unique_ptr<Node<int>> tmp_n1(new Node<int>{1, nullptr, nullptr});
unique_ptr<Node<int>> tmp_n2(new Node<int>{2, nullptr, nullptr});
unique_ptr<Node<int>> tmp_n3(new Node<int>{3, move(tmp_n1), move(tmp_n2)});
unique_ptr<Node<int>> tmp_n4(new Node<int>{4, nullptr, nullptr});
unique_ptr<Node<int>> root(new Node<int>{5, move(tmp_n3), move(tmp_n4)});

我希望实现的是摆脱临时变量,并在一个嵌套语句中初始化树。如果代码结构类似于树结构就好了。但是,下面的尝试失败,出现"could not convert"错误:

// second attempt: nested, but still a lot of syntax noise
unique_ptr<Node<int>> root(new Node<int>{5,
  new unique_ptr<Node<int>>(new Node<int>{3,
    new unique_ptr<Node<int>>(new Node<int>{1, nullptr, nullptr}),
    new unique_ptr<Node<int>>(new Node<int>{2, nullptr, nullptr})
  }),
  new unique_ptr<Node<int>>(new Node<int>{4, nullptr, nullptr})
});

有任何想法如何写这样的树初始化在一个语法干净,简洁,灵活的方式?

这是一个使用c++ 14 make_unique的解决方案,左子树和右子树的默认参数设置为nullptr,以避免原始的new:

#include <iostream>
#include <memory>
template<class T>
struct Node;
template<class T>
using node_ptr = std::unique_ptr<Node<T>>;
template<class T>
struct Node 
{
    T data;
    node_ptr<T> left, right;
    Node(T const& value, node_ptr<T> lhs, node_ptr<T> rhs)
    :
        data(value), left(std::move(lhs)), right(std::move(rhs))
    {}
};
template<class T>
auto make_node(T const& value, node_ptr<T> lhs = nullptr, node_ptr<T> rhs = nullptr)
{
    return std::make_unique<Node<T>>(value, std::move(lhs), std::move(rhs));    
}
template<class T>
void print_tree(Node<T> const& node)
{
    std::cout << "{ ";
    std::cout << node.data;
    if (node.left) {
        std::cout << ", "; 
        print_tree(*(node.left));
    }
    if (node.right) {
        std::cout << ", "; 
        print_tree(*(node.right));
    }
    std::cout << " }";
}
int main()
{
    auto const root = make_node(
        5, 
        make_node(
            3, 
            make_node(1), 
            make_node(2)
        ), 
        make_node(4)
    );    
    print_tree(*root);
}

实际示例也打印树。

Update:由于@Jarod42的评论,我将print_tree的签名更改为Node<T> const&,因此它现在是const-correct,您不必在任何地方键入.get()。我还制作了一个模板别名 node_ptr<T>,以便在实现中为unique_ptr<Node<T>>提供更简洁的符号。

感谢@Satus的评论,我能够想出一些(现在正在工作)辅助功能:

template <typename T>
unique_ptr<Node<T>> new_node(const T& data, 
                             unique_ptr<Node<T>> left = nullptr,
                             unique_ptr<Node<T>> right = nullptr) {
  return unique_ptr<Node<int>>(new Node<int>{data, move(left), move(right)});
}

允许这样构造一个树:

auto root =
  new_node(5,
    new_node(3,
      new_node(1),
      new_node(2)),
    new_node(4)
  );

我仍然不确定这是否是一个更有经验的c++ 11程序员会做的,但对我来说,这是朝着正确方向迈出的一步。