隐式对象创建是否适用于常量表达式?

Does implicit object creation apply in constant expressions?

#include <memory>

int main() {
    constexpr auto v = [] {
        std::allocator<char> a;
        auto x = a.allocate(10);
        x[2] = 1;
        auto r = x[2];
        a.deallocate(x, 10);
        return r;
    }();
    return v;
}

程序格式错误吗? Clang 这么认为,GCC 和 MSVC 不这么认为:https://godbolt.org/z/o3bcbxKWz


删除 constexpr 我认为该程序没有错误格式并且具有明确定义的行为:

通过 [allocator.members]/5 调用 a.allocate(10) 开始了它为其分配存储的 char[10] 数组的生命周期。

根据 [intro.object]/13 开始类型数组的生命周期 char 在其存储中隐式创建对象

char 等标量类型是隐式生存期类型。 ([basic.types.general]/9

[intro.object]/10 然后说 char 类型的对象是在 char[10] 数组的存储中创建的(并且它们的生命周期开始)如果这可以给程序定义的行为。

如果 char 对象的生命周期不在 x[2] 开始,没有 constexpr 的程序将由于在其生命周期之外写入 x[2] 而出现未定义的行为,但是由于上述参数,可以隐式创建 char 对象,从而使程序行为明确定义为以状态 1.

退出

对于constexpr,我想知道程序是否格式错误。隐式对象创建是否适用于常量表达式?

根据 [intro.object]/10 隐式创建对象以赋予程序 定义的行为,但格式错误是否算作定义的行为?

如果不是,则程序不应该是格式错误的,因为为 x[2].

隐式创建了 char 对象

如果是,那么下一个问题是是否未指定程序是否格式错误,因为[intro.object]/10也说如果多个集合可以给出,则隐式创建哪些对象是未指定的程序定义的行为。


从语言设计的角度来看,我希望隐式对象创建不应该发生在常量表达式中,因为验证一组使常量表达式有效的对象的(不)存在对于编译器来说可能是不可行的一般。

这里的 Clang 是不正确的。您已经引用了规范中使其格式良好的所有部分。 std::allocator<T>::allocateconstexpr;你得到一个指向 char* 的指针; allocator<T>::allocate 创建一个 T 的数组;创建 char 的数组隐式创建对象;访问 char 尝试导致 UB,但 IOC 阻止 UB,因此 UB 不会发生。因此:代码不允许格式错误。

Clang 声称完全支持 IOC 和 constexpr 分配器,所以这段代码应该可以工作。

Does implicit object creation apply in constant expressions?

所有表达式都是核心常量表达式unless [expr.const]/5 explicitly excludes it。那里没有提到可能是确定创建哪些对象的 UB 的操作,因此必须包括此类操作。

IOC 防止 表达式成为 UB。

I would expect that implicit object creation is not supposed to happen in constant expressions, because verifying the (non-)existence of a set of objects making the constant expression valid is probably infeasible for a compiler in general.

您忘记了对 constexpr 代码的其他限制。只要 [expr.const]/5 继续明确禁止 reinterpret_cast 和来自 void* 的转换,滥用 IOC 的方式就非常有限。例如,您不能将 allocate(10) 调用返回的指针转换为 int*。所以编译器知道 唯一可以在该存储中隐式创建的对象 chars.

所以在不断的评估时间,编译器可以只获取 allocator<char>::allocate 的结果并在返回它之前立即创建该数组的所有 char 成员。没有任何 constexpr 有效的方法可以获取该存储并隐式创建任何 other 而不是 chars.

并且在 T 不是字节类型时使用 allocator<T>::allocate 将不会在该存储中隐式创建对象。所以要么你只是得到一个指向未成形元素数组的指针,要么你得到一个指向字节类型数组的指针。

我猜 Clang 忘了检查这个特殊情况。

2469. Implicit object creation vs constant expressions

It is not intended that implicit object creation, as described in 6.7.2 [intro.object] paragraph 10, should occur during constant expression evaluation, but there is currently no wording prohibiting it.