Reputation: 19213
I understand that copying arbitrary chunks of memory is not always possible to do at compile time but since we are getting constexpr containers, virtual methods and also algorithms, why not memcpy too? It is too a kind of algorithm.
Furthemore,
std::bit_cast
seems a lot like std::memcpy
workaround reinterpret_cast
but it is constexpr
.std::copy
using iterators is marked as constexpr
for C++20, so copying is somehow possible for types.The usage would be to either copy or just "reinterpret" variables/arrays in constexpr
functions, the former is not solved by std::bit_cast
AFAIK. In particular, the question and my answer would like to use it.
std::bit_cast
can be constexpr but std::memcpy
cannot?std::bit_cast
and iterators in std::copy
.Relevant answer to C++20 bit_cast vs reinterpret_cast briefly cites from somewhere:
Furthermore, it is currently impossible to implement a constexpr bit-cast function, as memcpy itself isn’t constexpr. Marking the proposed function as constexpr doesn’t require or prevent memcpy from becoming constexpr, but requires compiler support. This leaves implementations free to use their own internal solution (e.g. LLVM has a bitcast opcode).
But it does not go into detail of not making it constexpr too.
Note, that I do not ask for why std::bit_cast
exists. I like it, it provides a clear intention instead of std::memcpy
workaround.
Upvotes: 15
Views: 5561
Reputation: 473946
The C++ object model in runtime code is generally treated somewhat loosely. It has fairly strict rules, but there are a bunch of backdoors that are either allowed or declared UB. The latter means that you can still write code to do it, but C++ guarantees nothing about the behavior of that code.
Within constant evaluation (aka: compile-time execution of code), this is not the case. The restrictions on constexpr
are specifically intended to allow the object model to be a real thing that you must follow, with no viable backdoors. And even the ones that it occasionally permits are explicitly required to be ill-formed and produce a compile-error, rather than being silent UB.
Basically at runtime, you get to treat memory as just bytes of storage. At compile-time, you can't; you're not allowed to. Even with dynamic allocation in constexpr
code added in C++20, you don't get to play a lot of the games you usually get to play with that sort of thing.
memcpy
deals in bytes of storage, copying them back and forth with no idea what they mean. bit_cast
knows both the source and destination objects, and it will not allow you to do it unless the source and destination objects are appropriate for bit_cast
ing (ie: trivially-copyable).
bit_cast
also has very specific restrictions on the content of both such objects if you want it to work at compile-time. In particular, you can't bit_cast
pointers or any objects containing pointers of any kind. Or references.
This is because pointers at compile-time are not just addresses. In order to catch UB, a compile-time pointer has to know the true dynamic type of the object it points to. So pointer conversions that just convert the address aren't allowed at compile-time.
Upvotes: 10
Reputation: 40862
That's more a comment then an answer as I'm only citing what is written in P0202R0: Add Constexpr Modifiers to Functions in and Headers, but I write it here as is does not fit the comments:
B.
std::memmove
andstd::memcpy
must have constexpr additions
std::memmove
andstd::memcpy
acceptvoid*
andconst void*
parameters. This makes them impossible to implement in pure C++ asconstexpr
, because constant expressions can not evaluate a conversion from typecv void *
to a pointer-to-object type according to [expr.const].
However those functions are not only popular, but also are widely used across Standard Library to gain better performance. Not making them constexpr will force standard Library developer to have compiler intrinsics for them anyway. This is a hard step that must be done.
The related section of [expr.const]
:
8.6 Constant expressions [expr.const]
[…]An expressione
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:
[…]
(2.13) — a conversion from typecv void*
to a pointer-to-object type;
Upvotes: 2