`std::vector` 的常数时间`contains`?

Constant time `contains` for `std::vector`?

我正在使用一些代码来检查 std::vector 是否在恒定时间内包含给定元素,方法是将其地址与描述 vector 数据范围的地址进行比较。但是我怀疑,尽管它有效,但它依赖于未定义的行为。如果 vector 不包含该元素,则不允许进行指针比较。

bool contains(const std::vector<T>& v, const T& a) {
  return (v.data() <= &a) && (&a < v.data() + v.size());
}

我认为这是未定义的行为是否正确?如果是这样,有没有办法在不显着改变代码的时间复杂度的情况下做同样的事情?

是的,如果引用未引用已经是向量元素的内容,则不允许进行所写的比较。

您可以通过将所有指针转换为 uintptr_t 并比较它们来定义行为。这将适用于所有具有连续内存的架构(即可能不是旧的 16 位 x86),尽管我不知道是否可以保证特定的语义。

附带说明一下,我总是将名称 contains 解释为与值有关,因此 非常 如果语义不是 std::find(v.begin(), v.end(), a) != v.end()。考虑使用更具表现力的名称。

您可以使用std::less

A specialization of std::less for any pointer type yields the implementation-defined strict total order, even if the built-in < operator does not.


更新:

The standard doesn't guarantee that this will actually work for contains though. If you have say two vectors a and b, the total order is permitted to be &a[0], &b[0], &a[1], &b[1], &a[2], &b[2], ..., i.e., with the elements interleaved.

正如评论中指出的那样,该标准仅保证 std::less 产生实现定义的严格全序,这与内置运算符强加的偏序一致。但是,该标准不保证指向不同对象或数组的指针的顺序。相关:https://devblogs.microsoft.com/oldnewthing/20170927-00/?p=97095

一件有趣的事是,在 Herb Sutter 的 gcpp 库 (link) 中也有类似的用法。有评论说它是可移植的,虽然这个库是实验性的。

    //  Return whether p points into this page's storage and is allocated.
    //
    inline
    bool gpage::contains(gsl::not_null<const byte*> p) const noexcept {
        //  Use std::less<> to compare (possibly unrelated) pointers portably
        auto const cmp = std::less<>{};
        auto const ext = extent();
        return !cmp(p, ext.data()) && cmp(p, ext.data() + ext.size());
    }