Reputation: 627
Let's imagine we have some class
struct Foo {
constexpr Foo(int& x) : x_(x) { x_++; }
constexpr ~Foo() noexcept { x_++; }
int& x_;
};
In C++20 with g++-10 -std=c++20
we can have a function like this:
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);
}
Let's try to divide operator new
into memory allocation and inplace construction.
So new function looks like this:
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;
}
But now there are two errors: our memory allocation new and placement new are not 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*)’
So let's try to work around these errors. With <memory>
we can have code like this:
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);
}
Question
What is the difference between my usage and usage of these operators in standard library?
Isn't the same thing happens in std::construct_at
and std::allocator<Foo>::allocate
under the hood?
Note
I tried to replicate std::construct_at by simply copying it implementation from <stl_construct.h>
but I got same error:
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*)
Upvotes: 7
Views: 1341
Reputation: 137425
What is the difference between my usage and usage of these operators in standard library?
Your usage is not called std::allocator<T>::allocate
or std::construct_at
.
These particular functions - along with a few others - are specifically granted exceptions to the normal rule:
For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to a member function of
std::allocator<T>
as defined in [allocator.members], whereT
is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression. Similarly, the evaluation of a call tostd::destroy_at
,std::ranges::destroy_at
,std::construct_at
, or std::ranges::construct_at does not disqualify E from being a core constant expression unless: [...]
As for plain new
expressions, during constant evaluation it never calls ::operator new
:
During an evaluation of a constant expression, a call to an allocation function is always omitted.
Upvotes: 10