我有一个类,它是容器的委托,并在内部存储一个迭代器到这个容器。
class A {
public:
list<int> m_data;
list<int>::iterator m_relevantDataStart;
A(const A & cpy) {
m_data = cpy.m_data;
m_relevantDataStart = cpy.m_relevantDataStart; //<--- UNWISE
}
};
现在的问题是,如果我尝试编写一个简单的构造函数来复制容器和迭代器,如上所述,迭代器在副本的上下文中变得不可用,更具体地说,我后来在尝试执行时遇到运行时异常比较:
`if(m_relevantDataStart == m_data.begin())` - Expression: list iterators incompatible
我认为这是因为m_relevantDataStart
仍然是我复制的类m_data
的迭代器,而m_data.begin()
指向原始容器的副本。
我发现this answer似乎有些相关,暗示指向原始容器的iterator
确实无法使用。
我的问题和 TL; DR:有没有办法将迭代器镜像到原始容器,这样“镜像”的结果会指向复制容器中的相应元素?
我可以想到一个解决方案,它需要确定原始容器中的项目索引(处理std::list
时的线性复杂性)并在复制容器中推进迭代器,但除非我使用了一些随机访问容器std::list
似乎效率很低。
我也总是可以选择编写自定义容器复制算法,我真的很想避免。
答案 0 :(得分:4)
我没有看到太多方法可以避免在复制开始时启动,并推进迭代器直到达到所需的点(只要你使用std::list
)。
如果您要自己复制列表,可以将该步骤合并到原始列表中,并在迭代器到达原始列表时保存正确的迭代器。
否则,复制列表,然后在新列表中推进迭代器所需的位数:
A(const A & cpy) {
m_data = cpy.m_data;
auto walker = cpy.m_data.begin();
m_relevantDataStart = m_data.begin();
while (walker != cpy.m_relevantDataStart) {
++walker;
++m_relevantDataStart;
}
}
当然,通过使用std::distance
查找原始列表中从开头到迭代器的距离,然后std::advance
(或std::next
,您可以“隐藏”一点复杂性)将迭代器移动到新的距离中 - 事实上,对于生产代码来说,这显然是可取的;上面的代码只显示了“在幕后”实际发生的事情。)
虽然这显然具有线性复杂性,但除非您正在处理真正的大型列表,否则它可能不会像最初显示的那样添加几乎与执行时间一样多的内容。由于您刚刚完成了整个列表的逐个节点副本,(至少大部分)原始列表和您刚刚创建的副本的数据通常都在缓存中,因此只需遍历它们需要从缓存中读取(而复制步骤更可能需要从主存储器中读取大部分数据)。
如果你正在处理那些(甚至可能)大到足以使整个事物可能不适合缓存的列表,那么第二次遍历就不会很便宜,你可以考虑将这两个部分复制一下,然后拼凑在一起:
auto m_data = std::list(cpy.m_data.begin(), cpy.m_relevantDataStart);
auto temp = std::list(cpy.m_relevantDataStart, cpy.m_data.end());
m_relevantDataStart = temp.begin();
m_data.splice(m_data.end(), temp);
鉴于m_list
和temp
将使用相同的分配器,拼接将具有恒定的复杂性,并且迭代器将在拼接中保持有效。
当然,如果你要从list
切换到vector
,那么所有这些(复制和获取正确的迭代器)都会使用更少的资源(但你还没有说够关于你的其他用途,猜测你可以从其他地方使用列表而不是vector或deque获得多少。