c++ - 使用 unique_ptr 实现的列表的迭代器

标签 c++ c++11 data-structures iterator unique-ptr

我正在创建一个使用 unique_ptr 的数据结构。我现在想在此数据结构上定义不同的迭代器,但是我的数据结构的节点是数据本身的一部分。因此,我希望迭代器返回实际节点,而不仅仅是其中包含的值。

这是我到目前为止得到的(非常简化的示例):

#include <algorithm>
#include <iostream>
#include <memory>

using namespace std;

template <typename T> struct node {
  node(T val) : val(val), next(nullptr) {}
  node(T val, unique_ptr<node<T>> &n) : val(val), next(move(n)) {}
  T val;
  unique_ptr<node<T>> next;

  template <bool Const = true> struct iter {
    using reference =
        typename std::conditional<Const, const node<T> *, node<T> *>::type;
    iter() : nptr(nullptr) {}
    iter(node<T> *n) : nptr(n) {}

    reference operator*() { return nptr; }

    iter &operator++() {
      nptr = nptr->next.get();
      return *this;
    }

    friend bool operator==(const iter &lhs, const iter &rhs) {
      return lhs.nptr == rhs.nptr;
    }

    friend bool operator!=(const iter &lhs, const iter &rhs) {
      return lhs.nptr != rhs.nptr;
    }
    node<T> *nptr;
  };

  iter<> begin() const { return iter<>(this); }
  iter<> end() const { return iter<>(); }

  iter<false> begin() { return iter<false>(this); }
  iter<false> end() { return iter<false>(); }
};

template <typename T> void pretty_print(const unique_ptr<node<T>> &l) {
  auto it = l->begin();
  while (it != l->end()) {
    auto elem = *it;
    cout << elem->val << endl;
    ++it;
  }
}

int main() {
  auto a = make_unique<node<int>>(4);
  auto b = make_unique<node<int>>(3, a);
  auto c = make_unique<node<int>>(2, b);
  auto d = make_unique<node<int>>(1, c);

  for (auto *elem : *d) {
    elem->val = elem->val - 1;
  }

  pretty_print(d);

  return 0;
}

以这种方式公开指向数据结构元素的原始指针是否被认为是不好的做法?这是否适用于更复杂的示例,尤其是关于 const - 正确性?

最佳答案

这主要是个人意见,但我认为这是个坏主意。 unique_ptr s 应该是唯一的;罕见的异常(exception)应该是,如果您需要将原始指针传递给其他未正确模板化的函数(并且您知道事实上它不会保留指针)。

否则,您处于合理使用的情况,例如初始化为 std::vector<node<int>*>使用你的迭代器,违反烘焙到 unique_ptr 中的假设.一般来说,您希望您的 API 的行为可预测,开发人员的头痛有限,而您的提议增加了“只要您不将任何东西存储到任何生命周期超出我的自定义结构的生命周期的任何东西,您就可以对其进行迭代”的头痛。

更好的选择是:

  1. 返回对您的实际 unique_ptr 的引用s(这样人们就可以在不违反唯一性契约的情况下与他们一起工作;如果他们不应该被改变的话,就把他们设为const);他们必须承担所有权或显式使用“借来的”非托管指针来存储结果,风险自负,但迭代可以正常工作,并且他们不会意外违反唯一性保证
  2. 商店 shared_ptr s 内部,并分发新的 shared_ptr s 在迭代期间,因此所有权会自动共享,只要保留单个共享指针,生命周期就会延长

重要的是,这两个选项都不允许有人意外地“做错事”。调用者可以做坏事,但他们必须亲自明确绕过智能指针保护机制才能这样做,这是他们的责任。

当然,第三种选择是:

  1. 返回对指向的值的引用,而不是指针;如果存储值,则应复制构造

用户可能会弄​​错最后一个(通过长期存储引用,获取它的地址以获得违反唯一性保证的新指针等),但它遵循现有的 C++ 约定(正如 Ryan 在评论中指出的那样)对于像 std::vector 这样的容器,所以这不是一个新问题; C++ 开发人员通常知道不要对从迭代中获取的引用做可怕的事情。 它可以说是最佳选择,因为它很好地映射到标准模式,使开发人员更容易适应现有的心智模型。

关于c++ - 使用 unique_ptr 实现的列表的迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39277153/

相关文章:

c++ - 我如何跨函数全局化和操作字符数组

c++ - 特化一个成员函数而不是整个类

c++ - 我应该尽可能在 C++11 中用 'const int' 替换 'constexpr int' 吗?

c++ - snprintf : Same code - different errors/warnings on different g++ compilers

algorithm - 动态凸包技巧

c++ - SSE 内在函数导致正常的浮点运算返回 -1.#INV

c++ - 带有 C++11+ unique_ptr 的 Libevent

c++ - 接受无限相同数量的 3 种类型的函数

algorithm - 网络流算法的适当图形表示

data-structures - OCaml中循环双向链表的实现