类别转发,双向和随机访问的迭代器需要是默认构造的。
为什么这样,为什么输入和输出运算符不必是默认可构造的?
答案 0 :(得分:14)
转发迭代器和更强的转义需要引用一些外部序列(参见[forward.iterators] / 6,其中说“如果a
和b
都是可解除引用的,那么a == b
如果并且只有当*a
和*b
绑定到同一个对象时才会使用。“)
这意味着它们通常只是一个轻量级的句柄(例如指向元素或容器中的节点的指针),因此没有理由不要求它们可以默认构造(即使默认构造创建一个奇异的迭代器,在分配新值之前不能用于任何东西)。所有非病态*前向迭代器都可以支持默认构造,依赖于此可以使一些算法更容易实现。
只满足输入迭代器或输出迭代器要求(并且没有更强)的迭代器可能包含由operator++
修改的状态,因此该状态可能无法构造为默认值。没有只对输入/输出迭代器进行操作的算法需要默认构造它们,所以它不是必需的。
答案 1 :(得分:3)
前向/双向/随机访问迭代器通常可以是指针 - 如果构造和初始化可能是代码发生的方式,那么构造和初始化可能会留下非本地化,它在历史上会帮助从指针使用代码迁移到迭代器。强制进行更多批量更改会让很多人试图将旧代码迁移到显式使用指针和迭代器上而感到沮丧。改变它现在会破坏很多代码。
输入和输出运算符通常最优雅地通过引用底层流或其他I / O对象来实现,并且引用必须在构造时初始化。当然,实现可能会被迫推迟,并在内部使用指针,但这肯定会使某些人以错误的方式 - 似乎太“C” - 所以标准促进使用引用并不令人惊讶。
答案 2 :(得分:3)
输入/输出迭代器:
Input Iterator:一旦InputIterator i递增,其先前值的所有副本都可能无效。
Output Iterator:在此操作之后,r不需要是可解除引用的,并且r的先前值的任何副本不再需要是可解除引用或可递增的。
如果我们看一下这个,很明显这些迭代器被设计成以最简单的方法使用。很像数组索引或简单指针将用于单程算法。 因此,确实没有必要使用默认构造函数。
但请注意,仅仅因为技术上不需要默认构造函数,如果您愿意,不会取消您实现它们的资格。
这些是需要默认构造函数的第一级迭代器,但为什么呢?
与历史编程原因有关的原因有很多,我相信这在某种程度上是有效的。在某种程度上,我认为委员会在迭代器和randomAccessIterator之间的某处需要实现默认构造,并且前向迭代器似乎是最佳选择。
但有一个很好的理由:
前向迭代器支持多遍算法,因此要求迭代器的副本在使用/递增迭代器后仍然有效。 如果这些副本仍然有效,则意味着算法将允许我们在某处“保存”它们。这也意味着我们保存它们的迭代器需要有一个默认/初始值。
考虑:
class MyClass
{
public:
void myFunction(ForwardIterator &i)
{
//do some code here
savedIter = i;
//do some code here
}
private:
ForwardIterator savedIter;
}
根据定义,这是有效的,因为我们被允许保存迭代器一段时间,因为要求这个迭代器的副本将保持有效。 (至少在迭代器指向的数据结构被销毁之前)
但是要创建这个类,ForwardIterator显然需要一个默认的构造函数...