使用 unique_ptrs 的基本转发列表

Basic forward list using unique_ptrs

作为学习 C++ 的练习,我想使用原始指针和 unique_ptrs 构建我自己的前向列表。使用原始指针,我有:

struct node_raw {
    node_raw(int data_, node_raw *next_) : data(data_), next(next_) {}
    int data;
    node_raw *next;
};

那我就可以这样写了

int main() {
    node_raw r1{1, nullptr};
    node_raw r2{2, &r1};
    node_raw r3{3, &r2};
}

获取转发列表:r3 -> r2 -> r1。

现在我想用 unique_ptrs 做同样的事情。

struct node_unique {
    node_unique(int data_, std::unique_ptr<node_unique> next_) 
        : data(data_), next(std::move(next_)) {}
    int data;
    std::unique_ptr<node_unique> next;
};

这是我目前的客户端代码:

int main() {
    node_unique u1{1, nullptr};
    node_unique u2{2, std::make_unique<node_unique>(std::move(u1))};
    node_unique u3{3, std::make_unique<node_unique>(std::move(u2))};
}

这给出了 u3.next->data = 2,所以它似乎有效。但这是对的吗?为什么我需要 std::move 两次才能创建一个新节点?现在查询 u1.data 是不是因为已经被移走了?基本上,使用 unique_ptrs 实现最小转发列表的规范方法是什么?

Why do I need std::move twice to make a new node?

您无法复制 node_unique,因为它已经隐式删除了 std::unique_ptr 成员的复制构造函数。

Is it now invalid to query u1.data because it has been moved from?

没有。隐式生成的移动构造函数将复制成员。读取复制自的int成员不无效。

But is it right?

节点 class 本身没有大问题1,但是您使用它的方式可能会产生误导。 u1u2 不是根为 u3 的列表的一部分。列表节点是 moved-from u1u2.

的浅拷贝

您不一定需要变量;由于列表元素是动态的,您可以直接创建它们:

node_unique root (
    3,
    std::make_unique<node_unique>(
        2,
        std::make_unique<node_unique>(
            1,
            nullptr
        )
    )
);

Basically, what is the canonical way to implement a minimal forward list using unique_ptrs?

“最小”是相对于您的要求而言的。您可以通过删除构造函数使节点成为 aggregate.

来使其更小

我不会说有一种“规范”的方式来实现转发列表,但它应该符合 Container 的概念。然而,那将是最小的。


1 除了析构函数具有线性递归深度这一事实外,正如 .

所指出的,这会导致中等大小列表的堆栈溢出

您应该实现一个自定义析构函数来迭代地销毁节点。这需要一点思考来防止智能指针析构函数钻研递归。