joh*_*lis 2 c++ complexity-theory range-v3 c++20 std-ranges
在最近介绍的 C++20 Ranges 中,我知道views通过使用视图适配器来实现可组合性。我也知道视图不拥有它们的元素,它们的性质是惰性的,也就是说它们只在需要时才进行实际计算。
视图如何实现O(1)移动、复制和分配操作的复杂性?那到我这里来的可能的答案是,意见是刚刚描述的“要计算”业务,他们只是指数据及其转换。
但是,听起来好像视图只是在表达我们的编码序列,并且只有在传递给一些急切的东西(例如算法)时,它们才会在这个特定的单个调用中表现出所有的计算负载。
后续问题:我可以理解如何实现O(1)副本,本质上是指可复制对象(尽管我不知道这是否是ranges::views这样做的)。但我无法理解这将如何在分配操作中发挥作用。同样,一个可能的答案是,因为所有这些都发生在编译时,那么再次“描述”赋值是一个O(1)操作。但是改变一个std::vector<int>被视图查看的,是一个运行时操作(很好的例子)。这还是O(1)手术吗?
您认识到视图不拥有它们引用或操作的元素。但是您似乎不明白这就是为什么这些操作是 O(1) 的原因。
如果你有这个:
vector<int> v = {...};
auto *vptr = &v;
auto *vptr2 = vptr;
vptr = vptr2;
Run Code Online (Sandbox Code Playgroud)
vref2相对于的初始化复杂度是v.size()多少?vptr相对于的赋值复杂度是v.size()多少?
它们都是 O(1) 因为它们只是复制指针。
vptr指向v; 它不拥有它。作为一个指针是如何不属于它v。这也是复制指针是 O(1) 操作的方式:因为指针的大小不关心它指向的数据的大小。
视图也是如此。视图类型存储基础范围的迭代器/哨兵。指针是一种迭代的,但迭代器的工作很像指针,它们指向到值的序列,而不是作为序列本身。范围由起始迭代器和表示该范围结束的值(有时是另一个迭代器,有时是可以针对迭代器进行测试的一般对象)定义。
迭代器类型不知道或不关心序列中有多少元素。迭代器概念对序列中的位置进行建模;从概念上讲,它不知道终点是什么。
因此,相对于它们指向的范围的大小,复制迭代器(通常)是 O(1)。移动和复制/移动分配也是如此。