嵌套的隐式生命周期类型未在常量评估中创建:这是clang的一个错误吗?
这个问题是对 [将非平凡创建的数组设为constexpr] 和 [在std::allocator提供的存储中,在任何显式构造之前访问隐式寿命类型的对象] 的后续探讨,在那里我在寻找一种方法来创建一个 constexpr 数组,元素类型不是默认可构造的。
解决方案,受到第一个问题答案的提示,是使用 std::allocator::allocate,它是 constexpr。
第二个问题涉及到该函数并不创建对象,而是创建对象的数组这一事实。不过,我在创建 std::array 的数组时,发现后者被隐式地创建为隐式寿命类型对象的子对象(即 std::array 的数组)的一部分。
但 测试它 时,代码被clang拒绝。
我可以把问题简化为这个mre:
#include <array>
#include <cstddef>
#include <iostream>
#include <memory>
template <typename T, std::size_t N>
constexpr T test() {
std::allocator<std::array<T, N>> allocator{};
auto* ptr = allocator.allocate(1);
ptr->data()[3] = T{42}; // clang disapprove
auto ret = ptr->data()[3];
allocator.deallocate(ptr,1);
return ret;
}
int main() {
constexpr std::size_t N = 50000;
[[maybe_unused]] constexpr auto val = test<int, N>();
}
LIVE
clang拒绝常量求值,声称 std::array 不在其生存期内:
note: member call on object outside its lifetime is not allowed in a constant expression 10 | ptr->data()[3] = T{42};
gcc和 msvc对这段代码没有问题。
这是clang的 bug吗,还是我关于 std::array 的创建判断有误?
解决方案
Clang的判断是正确的。
std::allocator<T>::allocate(n) 启动了 T[N] 的生存期,但并未启动其元素的生存期。 ([allocator.members]/5)。
你似乎假设 T 作为隐式寿命类型也会启动元素的生存期,但这在编译时并不起作用。
在你之前的问题中,你提到 [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.
但 allocate() 并未被描述为“隐式创建对象”,因此这条不适用于它。
allocate() 被说成在内部调用 operator new,该调用确实隐式创建对象,但不是在编译时: [intro.object]/16
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.
因此,std::array<...> 并非被隐式创建。