【问题标题】:C++ Iterator for C Linked Lists: to use range-based for loopsC 链表的 C++ 迭代器:使用基于范围的 for 循环
【发布时间】:2022-01-14 17:48:35
【问题描述】:

我正在使用旧版 C 代码,而新代码是用 C++ 编写的。为了使用 C++ 标准库,我在阅读了 Bjarne Stroustrup 在Adaptation 上的博客文章后,为旧版LinkedList 编写了一个简单的Iterator,如下所示。

我的问题是:

  1. 我想为另一个struct 创建另一个Iterator,比如struct TokenList。我不确定如何使用 namespace 并且仍然能够使用 range-based for 循环。任何指针都会有所帮助。

  2. Iterator 即:begin、end、++、*、!= 的适配器是否正确?目前,我有兴趣使用基于范围的for 循环阅读LinkedList 的内容。

Coliru

#include <cstdio>
#include <numeric>
#include <algorithm>

struct LinkedList {
    double v;
    LinkedList *next;
};

struct Iterator {
    LinkedList *current;
    LinkedList &c;
};

Iterator begin(LinkedList *c) { return Iterator {c, *c}; }
Iterator end(LinkedList *c) { return Iterator {nullptr, *c}; }
Iterator &operator++(Iterator &p) { p.current = p.current->next; return p; }
LinkedList *operator*(Iterator p) { return p.current; }
bool operator!=(Iterator lhs, Iterator rhs) { return (lhs.current != rhs.current); }

int main()
{
    LinkedList *node1 = new LinkedList;
    LinkedList *node2 = new LinkedList;
    LinkedList *node3 = new LinkedList;

    node1->v = 1; node1->next = node2;
    node2->v = 2; node2->next = node3;
    node3->v = 3; node3->next = nullptr;

    printf("// C style: iteration\n");
    for (auto ptr = node1; ptr; ptr = ptr->next) {
        printf("%e\n", ptr->v);
    }

    auto head = node1;
    // make use of begin(), end(), ++, != and *
    printf("// Modern C++ style: range based for-loop\n");
    for (const auto& it : head) {
        printf("%e\n", it->v);
    }

    delete node3;
    delete node2;
    delete node1;

    return 0;
}

【问题讨论】:

  • 实现一个人拥有的链表是一个有用的编程练习,几乎在每一个编程入门课程中都有。但是当一个人面临真正的任务时,简单地使用std::list 会简单得多。您能否阐明您正在尝试解决的具体问题,使用您自己的链表实现,不能简单地通过使用 std::list 来解决,以及随之而来的一切?
  • 旧代码似乎还是需要一些返工。 LinkedList 是链表中的一个节点。完全混乱。
  • 命名空间与此有什么关系?您的迭代器类应该属于您的容器,而不是全局范围级别。在迭代器中存储参考值是没有意义的。超级粗略。不要那样做。
  • 请注意您的删除不正确。您不能在已删除的节点上推进迭代器。
  • 获得基于范围的 for 循环的最佳方法是将 C 样式的链表包装在一个类中,该类执行与库容器相同的基本任务,并具有与图书馆容器。 Helpful link

标签: c++ iterator


【解决方案1】:

迭代器是伪指针类型。这意味着它们本身是有规律的。

struct Iterator {
  LinkedList *current;
  LinkedList &c;
};

在这里混合引用和指针。这是一个严重的反模式,赋值是做什么的?没有明智的答案。

我会完全删除 c 成员。

接下来你需要广播一个迭代器类型。你的看起来像一个前向迭代器。所有结束迭代器都可以相等。

Iterator begin(LinkedList *c) { return Iterator {c, *c}; }
Iterator end(LinkedList *c) { return Iterator {nullptr, *c}; }

这些看起来不错。只需删除*c。

请注意,名称不必是Iterator。 begin/end 必须在LinkedList 的命名空间中定义,但返回类型不必是。

Iterator &operator++(Iterator &p) { p.current = p.current->next; return p; }

我通常将其实现为成员函数,并实现前后递增; post是使用pre和copy实现的。

LinkedList *operator*(Iterator p) { return p.current; }

这是错误的。它应该以double&amp; 的形式返回*p.current。

bool operator!=(Iterator lhs, Iterator rhs) { return (lhs.current != rhs.current); }

当然。还将== 实现为!(lhs!=rhs)。

查找前向迭代器概念和前向迭代器标签。包括std::iterator_traits 所需的类型。

对于其他要迭代的东西,给迭代器一个不同的名字。这可以通过不同的命名空间。

如果不同的只是值的类型,你可以很容易地把它变成一个模板。那你只需要手动写begin/end。

如果v 的名称也发生变化,您可以在您编写的GetValue(List*) 函数上使用ADL 作为自定义点。


现在,在基于范围的 for 中可用与作为迭代器不同。基于范围的 for 更容易一些;但以上将您升级到完整的前向迭代器,这反过来又减少了您尝试使用 std 算法或基本上其他任何东西时的意外。


How I would write it:

// Iteration::LinkedListIterator<X> assumes that X is a linked list node
// with members ->next and ->value.  If it isn't, override the customization
// points GetNextNode and GetListElement in the namespace of X.

namespace Iteration {
  template<class List>
  List* GetNextNode( List* l ) {
    if (!l) return l;
    return l->next;
  }
  template<class List>
  decltype(auto) GetListElement( List* l ) {
    return l->value;
  }
  template<class List>
  struct LinkedListIterator {

    using self=LinkedListIterator;
    List *current;
    self& operator++(){ current = GetNextNode(current); return *this; }
    self operator++(int)&{ auto copy = *this; ++*this; return copy; }
    decltype(auto) operator*() {
      return GetListElement(current);
    }
    decltype(auto) operator*() const {
      return GetListElement(current);
    }
    auto operator->() {
      return std::addressof(GetListElement(current));
    }
    auto operator->() const {
      return std::addressof(GetListElement(current));
    }
    friend bool operator==(self const& lhs, self const& rhs) {
      return lhs.current == rhs.current;
    }
    friend bool operator!=(self const& lhs, self const& rhs) {
      return lhs.current != rhs.current;
    }

    using iterator_category = std::forward_iterator_tag;
    using value_type = std::decay_t<decltype(GetListElement(std::declval<List*>()))>;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type*;
    using reference = value_type&;
  };
};

struct LinkedList {
    double v;
    LinkedList *next;
};

// customization point; the name of 
double& GetListElement( LinkedList* l ) { return l->v; }
double const& GetListElement( LinkedList const* l ) { return l->v; }
Iteration::LinkedListIterator<LinkedList> begin( LinkedList* l ) {
  return {l};
}
Iteration::LinkedListIterator<LinkedList> end( LinkedList* l ) {
  return {nullptr};
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 2011-10-20
    • 2013-01-04
    • 2020-07-19
    • 1970-01-01
    相关资源
    最近更新 更多