Home > database >  Why do we need voidify function template in uninitialized_copy
Why do we need voidify function template in uninitialized_copy

Time:06-27

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:

  1. 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.

  2. 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.

  3. Why is the static_cast used here instead of some other cast.

  4. Why can't we just write return addressof(obj); or return (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.

  • Related