假设我们有课
struct Foo {
constexpr Foo(int& x) : x_(x) { x_++; }
constexpr ~Foo() noexcept { x_++; }
int& x_;
};
在带有g++-10-std=c++20
的C++20中,我们可以有这样一个函数:
constexpr int do_foo_1() {
int x = 0;
Foo* p = new Foo(x);
delete p;
return x;
}
int main() {
static_assert(do_foo_1() == 2);
}
让我们尝试将运算符new
分为内存分配和内部构造。新函数如下所示:
constexpr int do_foo_2() {
int x = 0;
Foo* p = static_cast<Foo*>(::operator new(sizeof(Foo)));
p = ::new ((void*) p) Foo(x);
p->~Foo();
::operator delete(p);
return x;
}
但是现在有两个错误:我们的内存分配new和放置new不是constexpr!
error: call to non-‘constexpr’ function ‘void* operator new(std::size_t)’
error: call to non-‘constexpr’ function ‘void* operator new(std::size_t, void*)’
因此,让我们尝试解决这些错误。使用
我们可以得到如下代码:
constexpr int do_foo_3() {
std::allocator<Foo> alc;
int x = 0;
Foo* p = alc.allocate(1);
p = std::construct_at(p, x);
std::destroy_at(p);
alc.deallocate(p, 1);
return x;
}
int main() {
static_assert(do_foo_3() == 2);
}
问题
我的用法和标准库中这些操作符的用法有什么区别?在std::construct_at
和std::allocator
中不是也发生了同样的事情吗?
备注
我试图通过简单地从
复制它的实现来复制std::construct_at,但我得到了同样的错误:
error: ‘constexpr decltype (...) my_construct_at(_Tp*, _Args&& ...) [...]’ called in a constant expression
error: call to non-‘constexpr’ function ‘void* operator new(std::size_t, void*)
我的用法和标准库中这些操作符的用法有什么区别?
您的用法不称为std::allocator
或std::construct_at
。
这些特定功能--以及其他一些功能--被特别授予正常规则的例外:
为了确定表达式E是否是核心常量表达式,对[allocator.members]中定义的std::allocator
的成员函数的调用的求值(其中t
是文字类型)不会取消E作为核心常量表达式的资格,即使这样的调用的实际求值否则将无法满足对核心常量表达式的要求。类似地,对std::destroy_at
、std::ranges::destroy_at
、std::construct_at
或std::ranges::construct_at的调用的求值不会取消E作为核心常量表达式的资格,除非:[...]
对于普通的new
表达式,在常数求值期间,它从不调用::operator new
:
在计算常量表达式的过程中,总是省略对分配函数的调用。