9

我或多或少得出的结论是,不可能编写一个其 value_type 未直接存储在容器中的符合容器。我认为这是不幸的,因为我经常希望我有容器,其中值类型要么是部分计算的,要么是由不连续的部分组装而成的(下面的例子,但与问题没有直接关系)。我知道如何编写使用代理对象的迭代器,尽管这很烦人。但我现在想知道 C++ 标准中是否真的有这些野兽的空间。这里可能有太多的废话;tl;dr 版本很简单:第 24.2.5 节第 1 和第 6 段的真正含义是什么,违反表观含义会在多大程度上破坏标准算法?或者,换句话说,如何解释它们以允许代理迭代器?

正如 Pete Becker 所指出的,实际上没有什么可以强迫我的容器符合标准库容器的要求。但是为了使用具有许多标准算法的容器,它必须具有至少具有 a 的一致迭代器forward_iterator_tag,或者它必须对此撒谎,但仍设法满足特定算法对其迭代器施加的操作(如果不是正式的)要求.

这是我的推理:

表 96 (§ 23.2.1),容器要求,包括:

Expression     Return type         Assertion/note
------------   -------------       ---------------------
X::iterator    iterator type       any iterator category
               whose value         that meets the
               type is T           forward iterator
                                   requirements.
                                   Convertible to
                                   const_iterator.

 a.begin()     iterator;
               const_iterator for
               constant a.

现在,转发迭代器:

§ 24.2.5,第。1:

一个类或指针类型X满足前向迭代器的要求,如果……

— ifX是可变迭代器,是对;reference的引用 TifX是一个 const 迭代器,reference是对const T

确实没有直接要求*a返回reference(其中a是 type X)。要求是:

如果可取消引用,则来自表 107(输入迭代器)*a必须“可转换为 T” 。a

来自表 106(迭代器)的*r类型必须具有类型且可取消引用。referencerX&

但是,表 106 还指定++r返回X&,因此*++r必须是reference。此外,(根据表 107)*a++必须是reference,尽管(表 109)a[n]只需要“可转换为参考”。我不得不说,我看不出*a哪里a是类型X*r哪里r是类型X&可能会有所不同,但也许我错过了一些微妙之处。

也许这里有一点回旋余地,但不多;在某些时候,您需要准备好创建一个T,如果您实际上在容器中没有一个,以便您可以提供对它的引用。

但关键是

§ 24.2.5,第。6(aandb是 type 的值X):如果aandb都是可解引用的,那么a == b当且仅当*aand*b绑定到同一个对象。

我找不到 的正式定义bound to,但在我看来,制作不可寻址对象的迭代器的常用策略是创建一个代理对象,通常存储在迭代器本身内。在这种情况下,除了禁止两个不同的迭代器对象之间进行相等比较之外,即使它们在逻辑上指示相同的位置,也需要非常宽泛地理解“绑定到”的含义以任何方式解释 24.2.5/6在容器中。

另一方面,我注意到应该知道的 Dietmar Kühl 在回答这个问题时说:

C++ 2011 的要求放宽了,迭代器不一定需要产生左值

那么,迭代器可以返回代理,还是不能?如果可以,这种代理的性质是什么?我认为这样的迭代器不符合标准的推理在哪里失败了?


正如所承诺的,一些容器的有效 value_types 不会连续存储在容器中:

1) 一个紧凑的关联容器,其键和值类型可以更有效地存储在两个单独的向量中。(将键保存在向量中还可以提高缓存友好性,并减少分配开销。)

2) Avector<T>伪装成map<integer_type, T>,简化了与其他map<X, T>类型的互操作性。

3)通过压缩其他几个容器形成的逻辑容器,产生一个逻辑值类型,它是tuple对压缩容器的值类型的引用。(在某些应用程序中,一个或多个压缩容器可能被完全计算,或者作为其他值的函数,或者作为序列号。)

4) 聚合类型容器的视图,它只有一些值。(很可能,底层容器和视图都是元组,其中视图元组的类型列表是底层容器类型的子集,可能以不同的顺序)。

我相信其他人可以轻松添加到此列表中;这些只是我在过去几个月中以某种方式破解的那些。

4

2 回答 2

2

最好的模型是std::vector< bool >. 它尽可能地接近兼容,但它的迭代器确实产生了代理。

该标准甚至指定这std::vector<bool>::reference是一个类。然而,容器需求表指定X::reference产生“T 的左值”。因此,它是严格不合规的。

但迭代器不限于T. 迭代器value_type必须是T并咨询输入迭代器要求,reference必须转换为value_type.

正如 Pete Becker 提到的,需求表是相当广泛的毯子,各个算法指定他们需要什么。只有需要reference真正成为参考的算法才会中断,这只是在说明显而易见的事情。

于 2012-12-17T06:10:15.117 回答
2

不要通过考虑“符合标准的容器”来限制自己:标准中没有任何东西取决于拥有一个。将容器要求视为描述标准中定义的容器要求的简写方式。而已。只要您的容器生成的迭代器是有效的,您就可以使用所有相应的算法,并且大概可以使用您自己编写的算法。

于 2012-12-13T23:59:40.437 回答