隐式对象的创建是否一定发生在存储返回的位置?

编程语言 2026-07-09

[intro.object] 似乎在隐式对象创建方面的说法,至少对于返回指针的操作,似乎发生在返回的位置(https://eel.is/c++draft/intro.object#14https://eel.is/c++draft/intro.object#16):

Further, after implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object. These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object.
...

Except during constant evaluation, any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects in the returned region of storage and returns a pointer to a suitable created object.

#include <cstddef>
#include <memory>
struct S {
    int i;
};
int main() {
    std::byte* storage = new std::byte[100];
    S* ptr = std::launder(reinterpret_cast<S*>(storage + sizeof(S)));
    ptr->i = 42;
    int ret = ptr->i;
    delete[] storage;
    return ret;
}

我对标准精神的理解是,隐式对象创建可以发生在存储中的任意位置,只要程序因此是良态的,但表述似乎在说反。

标准对隐式创建对象的位置的正确含义是什么?是否需要引用其他章节?

解决方案

[intro.object]/13描述了隐式对象创建的行为如下:

Some operations are described as implicitly creating objects within a specified region of storage. For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types.general]) in its specified region of storage if doing so would result in the program having defined behavior. If no such set of objects would give the program defined behavior, the behavior of the program is undefined. If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.

这一段并没有对在多少对象被创建以及它们在存储区域中何处被创建设定明确的限制,只要它们是某种对象组合,能够使程序具有定义行为即可。因此,可以在起始处创建一个对象,也可以在起始处之外创建对象,或两者同时存在。如果根本不需要创建对象,实现可能根本不创建任何对象。

但返回“合适创建对象”的操作还要遵循第14条的其他要求。我的看法是,措辞非常清晰:

[...] These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object. [...]

这意味着,与仅仅隐式创建对象的操作相比,返回一个合适创建对象的操作被保证在起始处隐式创建一个对象。它们也可能在起始处之外隐式创建零个或更多对象:注意句子明确地说了“one of the implicitly-created objects”,这暗示创建额外对象的权限并未被取消。

之所以像 malloc 这样的操作必须始终在起始处创建一个对象,是因为在C++中不存在那种尚未指向任何对象但允许你在那里创建对象的指针值。[basic.compound]/3 定义了四种指针值类型:指向对象/函数的指针、指向对象结束的位置的指针、空指针,以及无效指针。由于 malloc 不能返回后三类指针(特别是无效指针若将其赋给另一个指针变量,程序可能会崩溃;[conv.lval]/3.3),它必须在起始处创建一个对象,以便返回指向该对象的指针。如果你立刻在返回的指针处进行显式对象创建,那么 malloc 曾隐式创建的对象在本质上就无关紧要;你本来是看不见它的,但according to the standard,它确实创建了某些对象。

在OP的程序中,operator new[] 首先被调用,它在返回的存储起始处创建了至少一个对象,但你看不出它创建的是什么对象,因为 new-expression 立即在返回的存储中创建了100个 std::byte 对象,这会“覆盖” operator new[] 创建的对象。不过,启动一个 std::byte 数组的生存期的操作本身会触发隐式对象创建([intro.object]/16),因此此时在程序其余部分期望它存在的位置将隐式创建一个 S 对象。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章