如何正确(但有效地)实现像“vector :: insert”这样的东西? (指针别名)

时间:2012-08-20 02:52:34

标签: c++ aliasing

考虑vector的假设实现:

template<class T>  // ignore the allocator
struct vector
{
    typedef T* iterator;
    typedef const T* const_iterator;

    template<class It>
    void insert(iterator where, It begin, It end)
    {
        ...
    }

    ...
}

问题

我们在这里面临一个微妙的问题:
beginend可能会引用同一向量中的项 {/ 1>}后的

例如,如果用户说:

where

如果vector<int> items; for (int i = 0; i < 1000; i++) items.push_back(i); items.insert(items.begin(), items.end() - 2, items.end() - 1); 不是指针类型,那我们就没事了 但我们不知道,因此我们必须检查It是否未引用向量内的范围。

但是我们怎么做呢?根据C ++,如果引用相同的数组,那么指针比较将是未定义的!
所以编译器可能会错误地告诉我们这些项不是别名,实际上它们确实是这样,给了我们不必要的O(n)减速。

潜在的解决方案&amp;需要注意的

一种解决方案是每次复制整个 vector ,包含新项目,然后丢弃旧副本。

但是在上面的示例中,这种情况非常缓慢,我们只是为了插入1个项目而复制1000个项目,即使我们显然已经有足够的容量了。

是否有一种通用的方法(正确地)有效地解决了这个问题,即没有在没有任何别名的情况下遭受O(n)减速?

3 个答案:

答案 0 :(得分:6)

您可以使用谓词std::less等,这些谓词可以保证总订单,即使原始指针比较没有。

来自标准[comparisons]/8

  

对于templates,less,greater_equal和less_equal,任何指针类型的特化都会产生一个总顺序,即使内置运算符&lt;,&gt;,&lt; =,&gt; =不这样做。

答案 1 :(得分:0)

  

但是我们怎么做呢?根据C ++,如果他们引用相同的数组,那么指针比较将是未定义的!

错误。指针比较未指定,未定义。来自C ++03§5.9/ 2 [expr.rel]:

  

[...]可以比较相同类型的对象或函数(指针转换后)的指针,结果定义如下:

     

[...]
   - 其他指针比较未指定。

因此,在进行昂贵但正确的复制之前,测试是否存在重叠是安全的。

有趣的是,C99与C ++的不同之处在于,不相关对象之间的指针比较是未定义的行为。从C99§6.5.8/ 5:

  

当比较两个指针时,结果取决于指向的对象的地址空间中的相对位置。 [...]在所有其他情况下,行为未定义。

答案 2 :(得分:0)

实际上,即使它们是常规迭代器,这也是正确的。没有什么可以阻止任何人做

std::vector<int> v; 
// fill v 
v.insert(v.end() - 3, v.begin(), v.end());

确定它们的别名是否是任何迭代器实现的问题。

但是,您缺少的是您是实施,您不必使用可移植代码。作为实施,你可以做任何你想做的事。您可以说&#34;嗯,在我的实现中,我遵循x86并且<>可以用于任何指针。&#34;。那没关系。