提问者:小点点

constexpr函数中的New和delete


假设我们有课

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_atstd::allocator::allocal中不是也发生了同样的事情吗?

备注

我试图通过简单地从复制它的实现来复制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*)

共1个答案

匿名用户

我的用法和标准库中这些操作符的用法有什么区别?

您的用法不称为std::allocator::allocalstd::construct_at

这些特定功能--以及其他一些功能--被特别授予正常规则的例外:

为了确定表达式E是否是核心常量表达式,对[allocator.members]中定义的std::allocator的成员函数的调用的求值(其中t是文字类型)不会取消E作为核心常量表达式的资格,即使这样的调用的实际求值否则将无法满足对核心常量表达式的要求。类似地,对std::destroy_atstd::ranges::destroy_atstd::construct_at或std::ranges::construct_at的调用的求值不会取消E作为核心常量表达式的资格,除非:[...]

对于普通的new表达式,在常数求值期间,它从不调用::operator new:

在计算常量表达式的过程中,总是省略对分配函数的调用。