2017-06-19 53 views
2

我有一个类是一个容器的委托并在内部存储一个迭代器到这个容器。从原始容器镜像迭代器到它的副本

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.begin()指向原始容器的副本之类的m_data迭代器。

我发现this answer,这似乎有一些相关性,这意味着指向原始容器的iterator确实无法使用。

我的问题和TL; DR:有没有一种方法可以将迭代器镜像到原始容器,以便此“镜像”的结果将指向复制容器中的对应元素?

我能想到的一个解决方案,就需要在原来的容器确定项目指标和推进在副本容器中的迭代器(线性与std::list打交道时的复杂性),但除非我用一些随机存取容器,而不是std::list它似乎相当低效。

也总是有选择写一个自定义容器复制算法,我真的很想避免。

回答

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_listtemp将使用相同的分配器,接头将有不断的复杂性和迭代器都将在整个拼接仍然有效。当然,如果你要从list切换到vector,这将会使所有的(复制和获得正确的迭代器)使用更少的资源(但是你还没有足够的关于你的其他用途来猜测你可以从其他地方获得多少利用列表而不是向量或双向链表)。

+0

谢谢你的回答。在我的情况中,'list'是最合适的容器,因为两端的插入和删除非常频繁。 “距离”和“高级”解决方案是我计划采用的解决方案。我希望可能会有一些'list'函数的隐藏过载,它会为我做很脏的工作(可能在复制时),但是您向我保证没有真正优雅的解决方案。 – user35443

+0

@ user35443:如果你需要在两端插入/删除(但不在中间),你可能需要'std :: deque'而不是'std :: list'。它在两端提供了不断复杂的插入/删除操作,*和*随机访问迭代器。 –

+0

对不起,我的意思是(插入)和(清除两端):) – user35443