【问题标题】:Can you find the iterator for a list element given a pointer to the element?给定一个指向元素的指针,你能找到列表元素的迭代器吗?
【发布时间】:2014-06-26 04:56:46
【问题描述】:

这个问题可能是不言自明的,但是如果我有一个std::list<T> 对象,是否有任何标准方法来获取一个元素的迭代器,给定一个指向该元素的T* 值?

如果T 是用户定义的类型,似乎取消引用指针并使用find_if 可以完成这项工作,但这对我来说似乎效率低下。如果您尝试按值在容器中查找元素,那么您需要遍历容器直到找到某些内容是有道理的。但是如果你有一个指针,直觉上感觉应该有一个更直接的方法。我的基本理解使我认为列表项的迭代器和它的指针之间应该有一些一对一的关系,因为列表在 STL 中是双向链接的,但我真的没有太多支持这一点。

我对 C++ 迭代器不是很熟悉,所以如果有人能解释为什么有或没有办法做到这一点,那将会很有帮助。

编辑: Potatoswatter 提供了一个很好的 C++11 解决方案,但如果有任何 C++03 兼容的解决方案可用,我仍然很感兴趣。

【问题讨论】:

  • 我不明白这个问题 - 你是在问如何在列表中找到一个元素,给定一个指向要找到的元素的指针?
  • std::list<T> 持有T 类型的对象。给定一个std::list<T>::iterator,您可以获得对该对象的引用,您可以使用它来获取该对象的地址。但是,指针将不知道它指向的对象在std::list 中的位置。

标签: c++ list iterator


【解决方案1】:

没有标准算法可以在满足条件的范围内找到迭代器。 find 使用元素的值,但这根本不是一回事。 find_if 带有比较地址的 lambda 可以工作。

编写通用版本很容易:

template< typename iter, typename t >
iter iterator_from_ptr( iter first, iter last, t * ptr ) {
    while ( first != last ) {
        // For C++03 compatibility, use &* first instead of addressof.
        if ( std::addressof( * first ) == ptr ) return first;
        ++ first;
    }
    return last;
}

用法:iterator_from_ptr( my_list.begin(), my_list.end(), ptr )

正如你提到的,它是低效的,具体来说是 O(N)。加快速度违反了 C++ 的规则:您有一个指向 std::list 节点成员的对象的指针,而 list::iterator 实际上是指向 std::list 节点的指针。但是(通常)没有办法从指向成员的指针变为指向整个对象的指针。 (更不用说阻碍迭代器的抽象了。)

【讨论】:

  • std::addressof 不在C++03 中。我认为值得一提。
  • 感谢土豆瓦特的回复。我在最初的问题中没有提到这一点,但是 std::list&lt;T&gt; 没有很多元素,但 T 非常大,因此进行值比较可能会变得复杂并且可能会很慢。 O(N) 仍然有点令人失望,但对于我的特殊问题,它绝对足够好。
  • @Matt 正如答案本身所述,只需使用&amp;*first。只要T 不超载一元operator &amp; (颤抖) 你就没事。
【解决方案2】:

std::find() 中的 &lt;algorithm&gt; 是否适合您?如果值类型实现了operator==,它应该可以工作。

#include <algorithm>
auto it = std::find(list.begin(), list.end(), *ptr);

【讨论】:

  • 我不认为他想要那样。列表中可能有多个等于*ptr 的项目。他不想要第一个匹配*ptr。他想要指向地址为ptr的项目的迭代器。
  • 只有在后来的评论中在不同的答案中才变得清楚:-(
【解决方案3】:

虽然我不推荐以下功能,但此功能可以满足您的要求。它依赖于列表实现的知识,所以它真的只是一个有趣的练习,不应该依赖于生产代码:)

template <typename T>
typename list<T>::iterator iteratorFromPtr(T* a)
{
 auto offset = offsetof(_List_node<T>, _M_data);
 void* ptr = (void*)a;
 ptr -= offset;
 return _List_iterator<T>(static_cast<_List_node<T>*>(ptr));
}

编辑:使用 gcc-4.8.2 测试。

【讨论】:

    【解决方案4】:

    你必须在你的类中定义/实现迭代器。如果我不想实现迭代器,我通常会使用指针。

    【讨论】:

    • 这是std::list。它不是 OP 的类,它已经实现了一个迭代器。指针也不能作为双向链表的迭代器。
    猜你喜欢
    • 1970-01-01
    • 2018-03-30
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    相关资源
    最近更新 更多