通用的“越界”、“结束”迭代器

Posted

技术标签:

【中文标题】通用的“越界”、“结束”迭代器【英文标题】:generic "out of bounds", "past end" iterator 【发布时间】:2012-11-27 12:03:15 【问题描述】:

在我的应用程序中,我有一个(不平衡的)树数据结构。这棵树只是由“std::list of std::lists”组成 - 节点包含子节点的任意“列表”。使用它而不是单个列表使应用程序的其余部分变得更加容易。 (该程序是关于将移动节点从一棵树更改为另一棵树/树中的另一部分/到它自己的树)。

现在一个明显的任务是在“树”中找到一个子树。对于非递归搜索,它很简单:

subtree_iterator find_subtree(const N& n)          
    auto iter(subtrees.begin());
    auto e(subtrees.end());
    while (iter != e) 
        if ((*iter)->name == n) 
            return iter;
        
        ++iter;
    
    return e;

它返回一个指向子树位置的迭代器。然而,当我尝试实现多级搜索时,问题就开始了。即,我希望搜索hello.world.test,其中的点标记了一个新级别。

搜索正常

subtree_iterator find_subtree(const pTree_type& otree, std::string identify) const 
    pTree_type tree(otree);
    boost::char_separator<char> sep(".");
    boost::tokenizer<boost::char_separator<char> > tokens(identify, sep);
    auto token_iter(tokens.begin());
    auto token_end(tokens.end());

    subtree_iterator subtree_iter;
    for (auto token_iter(tokens.begin()); token_iter != token_end; ++token_iter) 
        std::string subtree_string(*token_iter);
        subtree_iter = tree->find_subtree_if(subtree_string);
        if (subtree_iter == tree->subtree_end()) 
            return otree->subtree_end()
         else 
            tree = *subtree_iter;
        
    
    return subtree_iter;

乍一看,它似乎“正确”工作,但是当我尝试使用它时,它失败了。使用它就像

auto tIn(find_subtree(ProjectTree, "hello.world.test"));
if (tIn != ProjectTree->subtree_end()) 
    //rest

但是,这会导致调试断言错误“列表迭代器不兼容”。这并不太奇怪:我正在比较来自不同列表的迭代器。但是我可以实现这样的事情吗?我的“备份”选项将返回一个std::pair&lt;bool,iterator&gt;,其中布尔部分确定树是否实际存在。除了使整个树单列表之外,还有其他方法吗?

【问题讨论】:

如果您决定将整个事情列在一个列表中,并不是说您可以使用list::splice 来移动它的各个部分。 @ymett hmm 忘记了那个-尽管在我的项目中,始终将节点视为其自身的树非常有用。使用单个列表来保存所有子树意味着 1 个节点是“根”和“特殊”。 【参考方案1】:

您不应该在内部使用迭代器。改用节点。

template <typename T>
struct Node 
 T item;
 Node<T>* next;
;

然后像这样将你的节点封装在一个迭代器外观中:

template<typename T>
class iterator 
private:
  Node<T>* node;
public:
  ...
;

然后使用一个通用的无效节点(当节点为 nullptr 时),只要达到或返回 end(),就会返回该节点。 请注意,我建议的是单链表(不是标准的双链表)。这是因为您不能从指向无效空节点的无效通用 end() 迭代器返回。 如果你不在你的算法中使用迭代器操作符--() 这应该没问题。

【讨论】:

所以基本上写我自己的迭代器类?这应该派生自 forward_iterator 类吗? 我不认为 forward_iterator 是标准的。所以你不需要。只要确保它符合 ForwardIterator 概念(这是非常基本的)。请参阅:en.cppreference.com/w/cpp/concept/ForwardIterator 好吧,只是尝试实现它,它打开了另一个完整的蠕虫罐:P。我认为通过使用该“节点”,您是要替换子树的 std::list-system 吗?但是,如果没有减量运算符,删除列表中间的节点是“昂贵的”。我想不可能将这样的自定义迭代器类与 std::list 容器一起使用? 并没有那么贵...只需将下一个节点复制到要删除的节点并删除下一个节点...您只需检查您是否没有删除最后一个节点...【参考方案2】:

std::vector&lt;list_iterator&gt; 堆栈遍历?其中堆栈的.back() 是唯一允许等于前一个堆栈的end() 的,而.front() 是根list 的迭代器?

【讨论】:

以上是关于通用的“越界”、“结束”迭代器的主要内容,如果未能解决你的问题,请参考以下文章

STL——容器(deque) 元素的存取&迭代器

为啥 std::variant 用开始和结束迭代器编译?

错误 1066:无法在 Pig 中打开别名的迭代器,通用解决方案

2022暑期复习-Day7

插入新元素时,结束迭代器是不是会更新? [复制]

什么是迭代器?