给出一个std :: list
std::list< int > myList
和该列表中元素的引用(或指针)
int& myElement | int* pElement
所以,基本上我知道那个元素的地址
如何有效地获得std::list<int>::iterator
该元素?
一个缓慢但有效的例子是
const_iterator it
for( it = myList.begin(); it != &myElement; ++it)
{
// do nothing, for loop terminates if "it" points to "myElem"
}
有更快的方法吗?像
const_iterator it = magicToIteratorConverter( myList, myElem )
对于矢量,您可以执行以下操作:
const int* pStart = &myVector[0] // address of first element
const int* pElement = &myElem; // address of my element
const idx = static_cast< int >( pElement- pStart ); // no need to divide by size of an elem
std::vector< int >::iterator it = myVector.begin() + idx;
答案 0 :(得分:5)
std::list<int>::iterator
不是int*
,您需要访问迭代器中的元素并获取其地址。此外,std::find_if
会为您处理大部分样板文件。
auto iter = std:find_if(myList.begin(), myList.end(),
[&myElement](const int & listElement)
{ return &myElement == &listElement; });
自己编写循环看起来像:
auto iter = myList.end();
for(auto i = myList.begin(); i != myList.end(); ++i)
if(&*i == &myElement)
{
iter = i;
break;
}
答案 1 :(得分:2)
给定一个列表,你只能从一端开始走过它,确保你不会超越结束。
const_iterator it, end;
for( it = myList.begin(), end = myList.end(); it!=end && it != &myElement; ++it)
{
// do nothing, for loop terminates if "it" points to "myElem"
// or if we don't find your element.
}
当然,您可以使用标准算法(如std::find)来查找它。
或者,您可以在插入时保持迭代器的持久性,并且在许多条件下它稍后仍然有效。
如果您想要查找速度,则应该使用列表以外的其他内容。
如果您有类似
的内容int x = 42;
int * this_might_be_handy = &x;
myList.insert(x);
myList现在有一个COPY数字 - 它的值为42,但位于不同的内存位置。
如果您在列表中保留指向int的指针,则会有所不同。从列表的front
获取值并查看地址将不会提供与x
相同的地址。
但你必须聪明地管理它们。
答案 2 :(得分:1)
这实际上是一个很好的问题,恕我直言,没有任何标准的方式来做OP所要求的。如果您了解列表节点看起来基本上像这样
struct list_node {
list_node* prev;
list_node* next;
T yourType;
}
如果你有一个指向yourType
的指针而不搜索整个容器,那么没有默认的方法可以到达节点(迭代器是指向节点的指针)。
由于标准没有帮助你必须弄脏你的手:
#include <list>
#include <iostream>
//This is essentially what you are looking for:
std::list<int>::iterator pointerToIter (int* myPointer) {
//Calculates the distance in bytes from an iterator itself
//to the actual type that is stored at the position the
//iterator is pointing to.
size_t iterOffset = (size_t)&(*((std::list<void*>::iterator)nullptr));
//Subtract the offset from the passed pointer and make an
//iterator out of it
std::list<int>::iterator iter;
*(intptr_t*)&iter = (intptr_t)myPointer - iterOffset;
//You are done
return iter;
}
int main () {
std::list<int> intList;
intList.push_back (10);
int* i1 = &intList.back ();
intList.push_back (20);
intList.push_back (30);
int* i3 = &intList.back ();
intList.push_back (40);
intList.push_back (50);
int* i5 = &intList.back ();
std::cout << "Size: " << intList.size () << " | Content: ";
for (const int& value : intList)
std::cout << value << " ";
std::cout << std::endl;
intList.erase (pointerToIter (i1));
intList.erase (pointerToIter (i3));
intList.erase (pointerToIter (i5));
std::cout << "Size: " << intList.size () << " | Content: ";
for (const int& value : intList)
std::cout << value << " ";
std::cout << std::endl;
return 0;
}
输出(以证明它按预期工作):
尺寸:5 |内容:10 20 30 40 50
尺寸:2 |内容:20 40
即使std :: list的实现对list-node使用不同的布局或者为它添加更多成员,这也能很好地工作。我还包括生成的汇编程序代码,以查看该函数实际上已减少到myPointer - 0x10
,这是64位机器上2个指针的大小。
汇编程序(至少-O1):
std::list<int>::iterator pointerToIter (int* myPointer) {
0: 48 8d 47 f0 lea rax,[rdi-0x10]
}
4: c3 ret
答案 3 :(得分:0)
这是上面Xatian非常有趣的解决方案的剖析,形式化(C ++ 11)和注释版本。该解决方案是O(1)(这是它如此有趣的原因之一),但是它假定了一些应该考虑的事情(Xatian对列表的STL实现的深入了解是它如此有趣的另一个原因。 )。
#include <iostream>
#include <list>
#include <cstdint>
int main(void)
{
// ASSUMPTIONS:
// 1.- nullptr == 0
// 2.- A std::list iterator is an object that contains just one thing: a pointer to the body of the iterator.
// 3.- A std::list iterator has a constructor that admits a pointer to a body provided by the user, which creates the iterator with that (assumed) existing body.
using DataType = int;
using DataList = std::list<DataType>;
std::cout << "Nullptr= " << reinterpret_cast<size_t>(nullptr) << std::endl;
std::cout << "Size of a pointer = " << sizeof(nullptr) << ", size of iterator = " << sizeof(DataList::iterator) << std::endl;
static_assert(reinterpret_cast<size_t>(nullptr) == 0,
"In this compiler, nullptr is not 0 and this will not work");
// we have a list filled with something.
DataList mylist{1,2,3,4};
// and an iterator pointing to some data.
DataList::iterator itaux{mylist.begin()};
++itaux;
++itaux;
// 1. calculate the offset of the data in a list iterator w.r.t. the beginning of the iterator body
DataList::iterator it{nullptr}; // call the iterator constructor. Nullptr becomes the address of the body where the iterator would store prev/next/data
// since nullptr is assumed to be 0, this is the same as to declare an iterator with its body at 0x00000000
DataType & itp = *it; // this is a reference to the user's data in the iterator body
// that iterator is a fake and does not contain any data, but since we are only dealing with addresses, no problem...
DataType * aitp = & itp; // this gets the address equivalent to the reference, which is at some point in memory from 0
size_t iteroffset = reinterpret_cast<size_t>(aitp); // That address becomes, actually, the offset of the data w.r.t. the beginning of the iterator body
std::cout << "Offset from iterator body start to data = " << iteroffset << std::endl;
// 2. we can get the pointer to the data from our existing iterator
DataType * mypointer = &(*itaux);
// 3. we can create a valid iterator from the pointer to the data
DataList::iterator iter;
*(reinterpret_cast<intptr_t*>(&iter)) = reinterpret_cast<intptr_t>(mypointer) - iteroffset; // the address of the beginning of the body (mypointer-iteroffset) is stored into
// the pointer to the body that the iterator actually is
std::cout << "pointed element: " << (*iter) << std::endl;
return(0);
}