非解除引用的迭代器是否超过了数组未定义行为的"一个接一个"的迭代器?

Jon*_*Mee 13 c++ arrays pointers iterator language-lawyer

鉴于int foo[] = {0, 1, 2, 3};我想知道指向过去"一个过去"的迭代器是否无效.例如:auto bar = cend(foo) + 1;

有大量的抱怨和警告,这是Stack Overflow问题中的"未定义行为",如下所示:c ++当过去结束迭代器时,迭代器+整数的结果是什么?不幸的是,唯一的来源是挥手.

我购买它的麻烦越来越多,例如:

int* bar;
Run Code Online (Sandbox Code Playgroud)

是未初始化的,但肯定不会调用未定义的行为,并且给定了足够的尝试,我确信我可以找到一个实例,其中未初始化的值bar具有相同的值cend(foo) + 1.

这里最大的困惑之一是我不会要求解除引用cend(foo) + 1.我知道这将是未定义的行为,标准禁止它.但是这样的答案:https://stackoverflow.com/a/33675281/2642059只引用解除引用这样的迭代器是非法的,回答这个问题.

我也知道C++只保证它cend(foo)是有效的,但它可能会numeric_limits<int*>::max()在这种情况下cend(foo) + 1溢出.我对这种情况不感兴趣,除非它在标准中被调出,因为我们不能让迭代器超过"一个接一个结束".我知道这int*只是一个整数值,因此会受到溢出的影响.

我想从一个可靠的来源引用一个引用,即将迭代器移到"一个接一个"的元素之外是未定义的行为.

Lig*_*ica 26

是的,如果你形成这样的指针,你的程序有不确定的行为.

这是因为你可以这样做的唯一方法是增加一个有效指针超过它指向的对象的边界,这是一个未定义的操作.

[C++14: 5.7/5]: 当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型.如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式.换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+N等效地,N+(P))和(P)-N(其中N值为n)分别指向第i + n和第i个元素数组对象,只要它们存在.此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素之后,如果表达式Q指向一个超过数组对象的最后一个元素,则表达式(Q)-1指向最后一个元素数组对象的元素.如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.

一个未初始化的指针不是同一个东西,因为除了声明它(显然是有效的)之外,你从未做过任何"获取"指针的事情.但是,如果不使用未定义的行为来编写程序,您甚至无法评估它(而不是取消引用 - 评估).直到您为其指定了有效值.

作为旁注,我不会称这些"过去的"迭代器/指针,这是C++中的一个术语,它特指的是" 一个过去的"迭代器/指针,它是有效的(例如cend(foo)它本身).你是waaaay过去的结束.;)

  • @Elkvis在这种情况下,实现必须确保数组不能以`numeric_limits <void*> :: max()`结尾. (4认同)
  • @JonathanMee`那不是真的,因为"一个过去的结尾"迭代器不是dereferencable`而是指向最后一个元素的迭代器,所以它保持前提条件并增加到"一个接一个"的结尾. (3认同)
  • @Elkvis该标准还保证指向"一个接一个"的指针始终有效.因此,不允许实现分配数组,使得迭代直到并包括"一个过去的结束"元素将导致溢出. (2认同)
  • @tkausl绝对是真的.它在整个标准中被称为"一个过去的结尾"是一个有效的迭代器/指针值(当然不是取消引用,只是地址有效.)你甚至可以在Orbit引用的Lightness Races中看到它. [5.7.5]:"......或者在数组对象的最后一个元素之后,评估不会产生溢出" (2认同)
  • @JonathanMee我不明白你的意思.克里斯在谈论的是增量迭代器是否可能,而不是一个过去的迭代器是否有效.而他所说的是,如果迭代器不是可以解除引用的话,你就不能增加迭代器,并且"一个接一个的" - 这个 - 也不是可以解除引用的,因此你可以_get to the the-to-the-end "-iterator(因为"end"-iterator是dereferencable")但不能再增加. (2认同)
  • @tkausl我们可能会说同样的话?当我说:"标准还保证指向"一个接一个"的指针始终有效." 我是说它对指向具有该地址的指针有效,而不是它可以被解除引用.你同意这一点,对吧? (2认同)
  • @Elkvis:是的,你做到了. (2认同)
  • @JonathanMee:呃,你是那个说过的人......(tkausl后来引用了你,并指出你错了 - 这种混乱的原因是为什么我们不应该使用代码格式来引用,即使在评论...). (2认同)

Ben*_*igt 5

TL;DR——计算迭代器超过最后一个迭代器是未定义的行为,因为在过程中违反了前提条件。


Lightness 提供了权威性地涵盖指针的引用。

对于迭代器,通常不禁止增加超过“结束”(最后一个元素的一个),但对于大多数各种迭代器是禁止的:

迭代器要求

输入迭代器要求

输入迭代器要求,特别是唯一可增加的 if dereferenceable子句,通过引用合并到前向、双向和随机访问迭代器中。

输出迭代器不受限制,它们总是可增加的。因为没有终点,越过终点的迭代器被定义排除在外,所以担心计算它们是否合法是没有实际意义的。

然后,在序列中向前跳跃是根据单独的增量定义的,因此我们得出结论,对所有迭代器类型而言,过去一个过去最后一个迭代器的计算要么毫无意义,要么是非法的

RandomAccessIterator 要求