容器的迭代器能否产生左值以外的其他内容?
Can a container's iterator yield something other than an lvalue?
我或多或少得出的结论是,不可能编写一个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
其中r
是X&
类型并且是可取消引用的。
但是,表 106 也指定++r
返回X&
,因此必须reference
*++r
。此外,(如表107所示),*a++
必须reference
,尽管(表109)a[n]
只需要"可转换为参考"。我不得不说,我不明白a
属于X
型*a
,*r
r
属于X&
型可能会有所不同,但也许我错过了一些微妙之处。
也许这里有一点回旋余地,但不多;在某些时候,你需要准备好创建一个T
,如果你实际上在容器中没有,以便你可以提供对它的引用。
但踢球者是
§ 24.2.5, 第6段 (
a
和b
是X
类型的值): 如果a
和b
都是可取消引用的,则当且仅当*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
真正成为参考的算法才会被破坏,这只是陈述显而易见的事实。
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- 成员函数不能为集合迭代器和const_iterator的输入重载(但可以为其他 STL 迭代器重载)
- 使用来自其他类的向量作为反向迭代器
- 容器的迭代器能否产生左值以外的其他内容?
- 将迭代器存储在临时容器上,然后从其他容器中删除它们
- 将迭代器取消引用到映射或 C++ 中的某些其他复合类型的类型是什么
- 为什么std::vector迭代器在重新分配其他向量时无效