容器的迭代器能否产生左值以外的其他内容?

Can a container's iterator yield something other than an lvalue?

本文关键字:其他 迭代器      更新时间:2023-10-16

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

正如 Pete Becker 指出的那样,实际上没有什么能迫使我的容器符合标准库容器的要求。但是为了使用具有许多标准算法的容器,它必须具有至少具有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满足前向迭代器的要求,如果...

— 如果X是一个可变迭代器,reference是对T

的引用;如果X是一个 const 迭代器,reference是对const T的引用

确实,*a没有直接要求返回reference(其中a属于X类型)。要求是:

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

从表 106(迭代器)中,*r必须具有类型reference其中rX&类型并且是可取消引用的。

但是,表 106 也指定++r返回X&,因此必须reference*++r。此外,(如表107所示),*a++必须reference,尽管(表109)a[n]只需要"可转换为参考"。我不得不说,我不明白a属于X*a*rr属于X&型可能会有所不同,但也许我错过了一些微妙之处。

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

但踢球者是

§ 24.2.5, 第6段 (abX类型的值): 如果ab都是可取消引用的,则当且仅当*a*b绑定到同一对象时,a == b

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

另一方面,我注意到迪特马尔·库尔(Dietmar Kühl)在回答这个问题时说:

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

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


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

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

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

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

4) 仅具有部分值的聚合类型的容器视图。(很可能,基础容器和视图都是元组,其中视图元组的类型列表是基础容器类型的子集,可能顺序不同)。

我相信其他人可以很容易地添加到这个列表中;这些只是我在过去几个月里以某种方式入侵的那些。

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

最好的模型是std::vector< bool >。它尽可能接近合规,但它的迭代器确实会产生代理。

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

但是迭代器并不绑定T。迭代器value_type必须T并查阅输入迭代器要求,reference必须转换为value_type

正如 Pete Becker 所提到的,需求表是相当宽泛的毯子,各个算法指定了它们的需求。只有需要reference真正成为参考的算法才会被破坏,这只是陈述显而易见的事实。