自 C++17 以来,具有正确地址和类型的指针是否仍然始终是有效指针?
(参考 这个问题和答案.)
在C++17标准之前,中包含了下面这句话[basic.compound]/3:
<块引用>如果类型 T 的对象位于地址 A,则称其值为地址 A 的类型为 cv T* 的指针指向该对象,无论该值是如何获得的.
但从 C++17 开始,这句话已经删除一>.
例如我相信这句话定义了这个示例代码,并且因为 C++17 这是未定义的行为:
alignas(int) unsigned char buffer[2*sizeof(int)];auto p1=new(buffer) int{};自动 p2=new(p1+1) int{};*(p1+1)=10;
在C++17之前,p1+1
保存着*p2
的地址并且类型正确,所以*(p1+1)
是一个指向 *p2
的指针.在 C++17 p1+1
是一个 指针越过结尾,所以它不是指向对象的指针,我相信它是不可解引用的.
这是对标准权利的这种修改的解释还是有其他规则可以弥补引用句子的删除?
解决方案这是对标准权利的这种修改的解释还是有其他规则来弥补这个引用句子的删除?
是的,这个解释是正确的.超过末尾的指针不能简单地转换为恰好指向该地址的另一个指针值.
新的 [basic.compound]/3 说:><块引用>
指针类型的每个值都是以下之一:
(3.1)指向对象或函数的指针(该指针被称为指向对象或函数),或
(3.2)超过对象末尾的指针([expr.add]),或
那些是相互排斥的.p1+1
是指向末尾的指针,而不是指向对象的指针.p1+1
指向 p1
处的 size-1 数组的假设 x[1]
,而不是 p2
>.这两个对象不是指针可相互转换的.
我们也有非规范性说明:
<块引用>[ 注意:超出对象末尾的指针 ([expr.add]) 不被视为指向可能位于该地址的对象类型的无关对象.[...]
阐明了意图.
作为 T.C.在众多评论中指出(尤其是这个),这确实是尝试实现 std::vector
所带来的问题的一个特例 - 即 [v.data(), v.data() + v.size())
需要是一个有效范围,但 vector
不创建数组对象,所以唯一的定义的指针算术将从向量中的任何给定对象到其假设的单一大小数组的末尾.如需更多资源,请参阅 CWG 2182、此标准讨论,以及关于该主题的论文的两次修订:P0593R0 和 P0593R1(特别是第 1.3 节).
(In reference to this question and answer.)
Before the C++17 standard, the following sentence was included in [basic.compound]/3:
If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained.
But since C++17, this sentence has been removed.
For example I believe that this sentence made this example code defined, and that since C++17 this is undefined behavior:
alignas(int) unsigned char buffer[2*sizeof(int)];
auto p1=new(buffer) int{};
auto p2=new(p1+1) int{};
*(p1+1)=10;
Before C++17, p1+1
holds the address to *p2
and has the right type, so *(p1+1)
is a pointer to *p2
. In C++17 p1+1
is a pointer past-the-end, so it is not a pointer to object and I believe it is not dereferencable.
Is this interpretation of this modification of the standard right or are there other rules that compensate the deletion of the cited sentence?
解决方案Is this interpretation of this modification of the standard right or are there other rules that compensate the deletion of this cited sentence?
Yes, this interpretation is correct. A pointer past the end isn't simply convertible to another pointer value that happens to point to that address.
The new [basic.compound]/3 says:
Every value of pointer type is one of the following:
(3.1) a pointer to an object or function (the pointer is said to point to the object or function), or
(3.2) a pointer past the end of an object ([expr.add]), or
Those are mutually exclusive. p1+1
is a pointer past the end, not a pointer to an object. p1+1
points to a hypothetical x[1]
of a size-1 array at p1
, not to p2
. Those two objects are not pointer-interconvertible.
We also have the non-normative note:
[?Note: A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. [...]
which clarifies the intent.
As T.C. points out in numerous comments (notably this one), this is really a special case of the problem that comes with trying to implement std::vector
- which is that [v.data(), v.data() + v.size())
needs to be a valid range and yet vector
doesn't create an array object, so the only defined pointer arithmetic would be going from any given object in the vector to past-the-end of its hypothetical one-size array. Fore more resources, see CWG 2182, this std discussion, and two revisions of a paper on the subject: P0593R0 and P0593R1 (section 1.3 specifically).
相关文章