I was reading about std::uninitialized_copy
and came across something called voidify
:
Effects: Equivalent to:
for (; first != last; result, (void) first) //#1----vvvvvvv----------->what does this call to voidify mean here ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(*first);
Upon further looking for voidify
I came to know that it is a function template:
Some algorithms specified in this clause make use of the exposition-only function voidify:
template<class T> constexpr void* voidify(T& obj) noexcept { //#2------vvvvvvvvvv-------------------------------->why does this const_cast to void* is needed return const_cast<void*>(static_cast<const volatile void*>(addressof(obj))); //#3------------------------^^^^^^^^^^^------------->why does this static_cast to const volatile void* is needed }
So as we can see that voidify
returns a void*
to obj
or something else I don't quite understand as there are some casts involved and i don't know why they are needed. I've placed comments indicating my doubts. Can someone tell me in steps why is voidify
needed in new (voidify(*result))
and why are the const_cast
and static_cast
needed.
In summary(just to clearly state my doubts) my questions are:
Why do we need to call
voidify
innew (voidify(*result))
. I know that this is using palcement-new but i don't know are we callingvoidify
and passing*result
as an argument and then using the return value for placement-new.Why the
const_cast
is used here. Why not some other cast likereinterpret_cast
orstatic_cast
. That is what is the need of choosingconst_cast
here.Why is the
static_cast
used here instead of some other cast.Why can't we just write
return addressof(obj);
orreturn (void*)(addressof(obj);
.
Note that I am trying to understand how the equivalent to
version works. The reason I asked my question in different parts is because I think that dividing it in individual steps and then understanding those individual steps lets us understand a topic better. At the end of the day I want to know how does the equivalent version
work.
CodePudding user response:
Why do we need to call voidify in new (voidify(*result)). I know that this is using palcement-new but i don't know are we calling voidify and passing *result as an argument and then using the return value for placement-new.
The first thing to point out is that in a lot of cases the code would work without voidify
, so if your intuition imagines it working without voidify, this is why. The voidify
is there to guard against the situation where there are multiple overloaded versions of operator new
, and one of them would be selected over placement new operator new(size_t, void*);
Why the const_cast is used here. Why not some other cast like reinterpret_cast or static_cast. That is what is the need of choosing const_cast here.
Why is the static_cast used here instead of some other cast.
If you want to be sloppy, you could also just do this with a single C-style cast, as in:
return (void*) addressof(obj);
But each of const_cast
and static_cast
does something different, and you could potentially need either or both types of conversion. You could replace static_cast
with reinterpret_cast
, but it wouldn't buy you anything and could make it harder to catch certain bugs, so at that point you might as well use the C-style cast.
Why can't we just write return addressof(obj); or return (void*)(addressof(obj);.
The former will fail unexpectedly if you happen to have an overloaded operator new(std::size_t, decltype(addressof(obj))
. The later (C-style cast) is perfectly fine as previously mentioned.
CodePudding user response:
Voidify() takes an address of an object that may be const
, volatile
, or both and creates just a void*
. This can't be done directly with just one type cast since it requires both the removal of qualifiers and casting to void
.
But it can be done in two steps. static_cast
can cast to a void*
but can't remove qualifiers. However, it can add any that aren't already there. Hence static_cast<const volatile void*>
The next step is to use const_cast<void*>
which removes the qualifiers leaving just a void*
Using these two casts has an additional benefit. It's valid code in a constexpr
function. reinterpret_cast
is not usable in constexpr
functions.