将C++数组指针的一个过端转换为interator合法吗

Is it legal to convert a one past end of C++ array pointer into an interator?

本文关键字:转换 interator 一个 指针 数组 C++      更新时间:2023-10-16

我有这个代码:

#include <set>
int main() {
    int array[] = { 0 };
    std::set<int> stdset(&array[1], &array[1]);
}

它获取最后一个数组元素之后的元素地址并将其转换为迭代器。与std::vector::end()基本相同。

这样做是合法的:

 std::vector<int> vec;
 std::set<int> stdset(vec.end(), vec.end());

因为"最后一个"迭代器是一个非包容性的限制。

对原始数组执行与第一个代码片段中相同的操作合法吗?

您可以在C++中的任何数组的末尾取一个的地址。

您不能取消引用并使用结果,但可以将其与指向同一数组的其他指针以及经过同一数组末尾的其他指针进行比较。

您在OP中所做的是已定义的行为,并且表示一个空范围。

不涉及转换。指针支持与随机访问迭代器相同的操作(一元*运算符、+++运算符等),因此可以用作迭代器。采用迭代器的标准库函数是在迭代器类型上模板化的,因此它们将采用指针而不将其转换为任何内容。

这一点,再加上指向数组末尾的指针是有效的(只要你没有取消引用它),意味着你的代码是正确的。

您没有转换任何内容:指针是向量上的有效迭代器。

将指针指向刚好超过数组末尾的元素是合法的,但取消引用这样的指针是不合法的。

ANSI C,§5.7第5段:

将具有整型的表达式添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的一个元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果数组元素和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+N(等价地,N+(P))和(P)-N(其中N的值为N)分别指向数组对象中的第i+N个和第i-N个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象最后一个元件之后的一个,并且如果表达式Q指向数组对象最近一个元件之前的一个元件,那么表达式(Q)-1指向数组对象最后一个元件。如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象最后一个元素之后的元素,则评估不应产生溢出;否则,行为是不确定的。

我在网上的C++标准报价中没有找到这方面的参考,我也没有C++标准的副本,所以我不能证明它仍然是关于C++的最新版本,但它可能是。

TL;DR:您的代码是合法的。

老实说,根据任何规格,我都不知道你的问题的答案。尽管如此,我在您的方法中看到的问题是,您将实现与您正在使用的库中的实现细节联系在一起。

如果您将实现与所使用的库所公开的接口绑定在一起,那么如果这些库的实现发生更改,您的消费代码就不太可能被破坏。在这种特殊情况下,这可能不是很相关,因为数组的内存布局在不久的将来不太可能发生变化,但如果发生了变化,运行库开发人员也可能相应地更改迭代函数的实现,因此如果您使用公开的函数,您的代码应该按预期继续工作。然而,如果您的代码依赖于库的实现细节,那么可能是您必须查看所有案例使用并相应地更改它们。

编辑:

对不起,我想我表达得不太清楚;我说的不是你的代码,而是你的方法。封装的好处之一是,它允许编写出色执行每个任务的代码组件,然后通过组合多个代码组件提供的功能来编写应用程序。具有多个抽象级别使我们能够设计上层,而不必担心下层的微小细节。

如果组成整个应用程序的不同组件彼此的实现细节保持隔离,那么只要组件保持其最小接口并且其实现行为正确,就可以在不破坏兼容性的情况下轻松升级组件。如果不同的组件在实施过程中相互依赖,那么升级就会变得更加困难,因为需要根据相关组件的内部结构进行更改;在一个较低级别的组件中进行看似无害的修改(比如在两个旧的组件之间插入一个新的成员变量),可能会在一段完全不相关的代码中"巧妙地"依赖于组件的内部结构而产生完全不可预见的后果。

在编程艺术中,你可以使用你找到的任何资源来将你的输入转化为你的输出,但这并不意味着所有的可能性都有相同的含义。如果您担心执行时间,并且调用函数的开销是不可接受的,那么您可能会通过完全跳过面向对象的方法并按索引迭代数组来获得一些额外的周期。如果执行时间不是那么关键,以至于它允许调用接口上的公共方法,那么通过使用它,您将获得更小的升级噩梦。