Why is std::launder required here?

1k Views Asked by At

I was reading cppreference and in the example for std::aligned_storage is had this example of a vector/array class:

template<class T, std::size_t N>
class static_vector
{
    // properly aligned uninitialized storage for N T's
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
    // IF you want to see possible implementation of aligned storage see link.
    // It's very simple and small, it's just a buffer
    
    std::size_t m_size = 0;
 
public:
    // Create an object in aligned storage
    template<typename ...Args> void emplace_back(Args&&... args) 
    {
        if( m_size >= N ) // possible error handling
            throw std::bad_alloc{};
 
        // construct value in memory of aligned storage
        // using inplace operator new
        new(&data[m_size]) T(std::forward<Args>(args)...);
        ++m_size;
    }
 
    // Access an object in aligned storage
    const T& operator[](std::size_t pos) const 
    {
        // note: needs std::launder as of C++17
        // WHY
        return *reinterpret_cast<const T*>(&data[pos]);
    }
 
    // Delete objects from aligned storage
    ~static_vector() 
    {
        for(std::size_t pos = 0; pos < m_size; ++pos) {
            // note: needs std::launder as of C++17
            // WHY?
            reinterpret_cast<T*>(&data[pos])->~T();
        }
    }
};

Essentially each bucket/area/address of memory where each element resides is a char buffer. At each element position that exists a placement new is done in that buffer of the T type. So that when returning the position of that buffer that char buffer can be cast to T* because a T was constructed there. Why is std::launder required from C++17 onwards? I have asked a bunch of questions about this and it seems agreed that:

alignas(alignof(float)) char buffer [sizeof(float)];
new (buffer) float;// Construct a float at memory, float's lifetime begins
float* pToFloat = buffer; // This is not a strict aliasing violation
*pToFloat = 7; // Float is live here

This is not a strict aliasing violation apparently, it's completely fine. So therefore why is std::launder required here?

1

There are 1 best solutions below

11
On
    // note: needs std::launder as of C++17
    return *reinterpret_cast<const T*>(&data[pos]);

Why is std::launder required from C++17 onwards?

The conditions through which you can dereference a pointer from reinterpret_cast are minimal. This is not one of them. You are casting a pointer to one type to a pointer to a completely unrelated type, and reading through it. See here.

std::launder allows you to derive a valid pointer. It's whole reason for existence is to give you this.

Prior to C++17, you would have had to save the pointer returned from placement new, as that was the only valid pointer to T in sight, and before std::launder you could not make one from the buffer.

I have asked a bunch of questions about this and it seems agreed that... This is not a strict aliasing violation apparently, it's completely fine. So therefore why is std::launder required here?

That is not fine, for the same reason. Nor is it agreed that it's fine. See @eerorika's answer on your question here.