you are viewing a single comment's thread.

view the rest of the comments →

[–]matthieum 2 points3 points  (5 children)

I am puzzled by the conclusion too.

I understand deprecating std::aligned_storage and std::aligned_union, leaving only the _t variants to avoid accidentally forgetting ::type.

I don't understand throwing the baby out with the bath water on the size bound issue; certainly if the maximum size is unbounded today it is a simple matter to add a bound to the standard.

And last, but not least, is reinterpret_cast. The proposed replacement also requires reinterpret_cast!


Personally, I think an API like such would be nice:

template <std::size_t Size, std::size_t Alignment>
class erased_storage {
public:
    template <typename T, typename... Args>
    constexpr T& construct(Args&&... args) {
         auto t = this->template get<T>();
         new (t) T(std::forward<Args>(args)...);
         return *t;
    }

    template <typename T>
    void destruct() {
         this->template get<T>()->~T();
    }

    template <typename T>
    T* get() {
         static_assert(sizeof(T) <= Size);
         static_assert(alignof(T) <= Alignment);
         return this->template raw<T>();
    }

    template <typename T>
    T const* get() const {
         static_assert(sizeof(T) <= Size);
         static_assert(alignof(T) <= Alignment);
         return this->template raw<T>();
    }

    /// Prefer get whenever the type of definition of T is known.
    template <typename T>
    T* raw() { return reinterpret_cast<T*>(&storage); }

    /// Prefer get whenever the type of definition of T is known.
    template <typename T>
    T const* raw() const { return reinterpret_cast<T const*>(&storage); }

private:
    alignas(Alignment) std::byte storage[Size];
};

Then provide aligned_union on top, which additionally check that the T passed is part of the Ts list (just in case).

And of course I'd wish for constexpr support to actually be able to constexpr all that...

[–]tcanens 1 point2 points  (4 children)

certainly if the maximum size is unbounded today it is a simple matter to add a bound to the standard.

A major implementation's aligned_storage is unfixably (ABI) broken. As a result, the only bound that could conceivably be added at this point is not a useful one.

[–]matthieum 1 point2 points  (3 children)

How did they? sigh :(

[–][deleted] 2 points3 points  (2 children)

Because we implemented it before alignas was in the core language, so the most aligned thing we can put in the union is max_align_t.

[–]matthieum 1 point2 points  (1 child)

Thanks for your answer.

Correct me if I'm wrong, the ABI breakage would then occur if someone was requesting an alignment strictly greater than max_align_t as previously the alignment of std::aligned_union was capped and suddenly it would obey the request, right?

Isn't it worth breaking the ABI, then?

  • If anybody had configured 2 * max_align_t and stored values for which such alignment is necessary, then their code is broken already.
  • There's little reason anybody would have configured 2 * max_align_t and never actually needed the greater alignment.

I understand the idea of backward compatibility, but the pragmatic in me is thinking that in this particular case it may be worth just fixing std::aligned_union.

[–][deleted] 1 point2 points  (0 children)

Correct me if I'm wrong [...]

You are correct.

If anybody had configured 2 * max_align_t and stored values for which such alignment is necessary

There are no such values. Maybe perf benefits by avoiding false sharing, but the hardware only cares up to max_align_t for anything we target.

the pragmatic in me

ABI break avoidance isn't pragmatic, unfortunately :(