Date: Sun, 21 Jul 2024 19:34:28 +0200
Invasive/in-band signaling can and is used here and there in C++.
Just create a type trait, not a core language feature, and template classes can use this property.
But
- sometimes the free value is not stored as binary zero
- open question: may classes like optional just use that property? Perhaps the null is already used for something else by the class itself or by others
- You would need lots of different in-band-signals, perhaps the next class wants to store more than one possible value
- It saves memory, but could also slow down some accesses, they could need additional checks, decoding
-----Ursprüngliche Nachricht-----
Von:Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]>
Gesendet:So 21.07.2024 18:51
Betreff:[std-proposals] all bytes zero == null object
An:std-proposals <std-proposals_at_[hidden]>;
CC:Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>;
Typically when implementing a class such as 'std::optional', the size
of 'std::optional<T>' is equal to "sizeof(T) + 1u", because one more
byte is needed to store a boolean to indicate whether there is a valid
object in existence.
We can use "__datasizeof" or "[[no_unique_address]]" in order to try
use the tail padding of T to store the boolean, and if we succeed,
then sizeof(optional<T>) == sizeof(T).
If T has no tail padding, then we need an extra byte to store the
boolean. Unless . . . we had the ability to mark the class
'null_bytes==null_object" as follows:
class T (null_bytes==null_object) {
. . .
};
Marking a class as 'null_bytes==null_object' means that a byte-pattern
of all zeroes is a 'null object'. The standard header <type_traits>
would then have "std::is_null_bytes_null_object".
So then "std::optional::has_value" could be:
bool optional::has_value(void) noexcept requires
is_null_bytes_null_object_v<T>
{
return (0u == *storage) && !memcmp(storage, storage + 1u,
sizeof(storage) - 1u);
}
Another effect of marking a class as 'null_bytes==null_object' would
be that the destructor doesn't get called. For instance the following
program won't crash:
struct T (null==0) {
int a, b, c;
~T(void) { *(int*)nullptr = 666; }
};
int main(void)
{
T var = {0,0,0};
}
The above program doesn't have undefined behaviour, because the
destructor is treated as though it were written:
~T(void)
{
char const *const p = (char*)this;
if ( (0u == *p) && !memcmp(p, p + 1u, sizeof(*this) - 1u) ) return;
*(int*)nullptr = 0;
}
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2024-07-21 17:34:31