【问题标题】:C++ Contiguous Sequence ConceptC++ 连续序列概念
【发布时间】:2013-03-03 17:53:21
【问题描述】:

C++ 标准库提供了多种“概念”,用于指定容器对象的接口。例如,std::vector 实现了ContainerSequenceRandomAccessContainerReversibleContainer 概念。

是否有一个在 C++03 或 C++11 中指定的概念,它描述了一个 Sequence,它保证了元素之间的连续内存,因此:

static_cast<void*>(&some_sequence[N]) == static_cast<void*>(&some_sequence[0] + N)>

这将是一个有用的概念,因为它告诉您是否可以将 Container 与任何需要连续内存缓冲区的函数一起使用,例如 std::istream::read

我知道,在实践中,只有 std::vector(我认为仅在 C++11 中的 std::string)实际上保证了底层的连续缓冲区 - 但这种保证是 std::vector 独有的还是有一个定义的“概念”表示提供连续内存的通用Sequence 类?

【问题讨论】:

  • 据我所知,没有这个概念。 std::vectorstd::stringstd::array 只是在 [0, c.size()) 中具有 c.data() + i == &c[i] for i 的不变量。
  • @Xeo:对。没有概念,只是要求元素(或char_types)“连续存储”
  • @Xeo 别忘了std::valarray :)
  • @Mihai:哈哈,被遗忘的兄弟。
  • 我认为答案是否定的。 RandomAccess 可能是最接近的,但 std::deque 是明显的反例。

标签: c++ c++11 containers std language-lawyer


【解决方案1】:

“连续容器”在 C++17 中指定。来自$23.2.1/13 General container requirements [container.requirements.general]

连续容器是支持随机访问迭代器 ([random.access.iterators]) 并且其成员类型 iterator 和 const_iterator 是连续迭代器 ([iterator.requirements.general]) 的容器。

关于“连续迭代器”,$24.2.1/5 In general [iterator.requirements.general]

进一步满足以下要求的迭代器,对于整数值 n 和可解引用的迭代器值 a 和 (a + n),*(a + n) 等价于 *(addressof(*a) + n),称为连续迭代器。

std::vectorstd::vector<bool> 除外)、std::arraystd::basic_string 是连续容器。

【讨论】:

  • 不错。是否明确说明了哪些容器是连续的?我认为std::valarray 和plain 应该在列表中。普通数组呢?
  • @JohanLundberg 我尝试搜索标准,似乎只有我的答案中提到的 3 个容器是“连续容器”。 std::valarray 和普通数组不是“连续容器”,因为它们是“连续”而不是“容器”。
【解决方案2】:

我发现自己必须多次识别满足此功能的类型。我不知道发明这样一个特殊的“概念”是否优雅(想象它是一个涉及记忆的概念,不是很抽象),但我同意这样的东西会有用。

同时为了实用并将此概念/要求转换为纯语法要求,让我们倒退。如果我们将自己限制在标准中,那么保证(或几乎保证)连续性的类是什么?按相关性排序:

std::vector<T>
T[N] // !!
std::array<T, N>
std::string
std::initializer_list<T>
std::valarray<T>

在所有这些中,std::vectorstd::arraystd::string 有一个名为 .data() 的成员函数。所以,如果这对你来说足够了,可以依靠成员 .data() -&gt; T* 的存在来指示连续内存。

你有两个选择:

1) 如果类型不连续,请努力使用成员函数.data() 来引发语法错误。 (如果您将 t[0] 替换为 *t.data(),则不难)

2) 在.data() 上使用某种 SFINAE。

template<class ContiguousSequence, typename = decltype(std::declval<ContigiousSequence>().data())>
void fun(ContiguousSequence&& s){...} // this function will only work with contiguous data

此外,C++17 具有std::data,它可以将其推广到所有具有.data() 的类型,另外还有T[N]std::initializer_list&lt;T&gt; 的重载。 因此,您可以将....data() 替换为上面的std::data(...)

结论,我认为这是一个很好的约定是 如果一个类型有一个 data 函数(或 C++11 中的 .data())返回一个指向值类型的指针,那么元素 是连续的。

(好吧,std::valarray&lt;T&gt; 怎么样?它不起作用,除非你重载 std::data(std::valarray&lt;T&gt;&amp;)。但是谁使用std::valarray?我认为这是一个相当废弃的 C++ 角落)

最后,请注意例如std::map 和不太明显的std::deque 没有.data()(或std::data(...))函数。 boost::multi_array&lt;..., N&gt; 有一个 .data() 成员并返回一个指向数组元素的指针,不清楚这是否是您想要的意义上的连续序列(因为顺序不明显),但在某种意义上它也是一个连续分配内存。

编辑: 目前有两个提案解决了这个问题(但在迭代器级别)http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3884.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4284.html

【讨论】:

    【解决方案3】:

    从 C++03 开始​​,只有 std::vector 保证 (23.2.4.1):

    向量的元素被存储 连续的,这意味着如果 v 是 向量,其中 T 是一些 类型不是 bool,那么它服从 恒等式 &v[n] == &v[0] + n 为 所有 0

    C++11 添加了 std::array,它是固定大小数组的包装器,并且也具有相同的属性。我认为没有办法知道任何容器是否具有此类属性。

    【讨论】:

      猜你喜欢
      • 2012-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      • 1970-01-01
      • 1970-01-01
      • 2018-05-31
      相关资源
      最近更新 更多