@@ -884,10 +884,26 @@ auto make_observable(Args&&... args) {
884884 constexpr std::size_t obj_size = sizeof (object_type);
885885 constexpr std::size_t obj_align = alignof (object_type);
886886 constexpr std::size_t obj_offset = obj_align * (1 + (block_size - 1 ) / obj_align);
887- constexpr std::size_t alloc_align = block_align > obj_align ? block_align : obj_align;
888887
889- std::byte* buffer = reinterpret_cast <std::byte*>(operator new (
890- obj_offset + obj_size, std::align_val_t {alloc_align}));
888+ // See comment below on alignment
889+ static_assert (
890+ block_align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__,
891+ " control block is over-aligned, this would require a custom allocator" );
892+ static_assert (
893+ obj_align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__,
894+ " object is over-aligned, this would require a custom allocator" );
895+
896+ // NB: The correct thing to do here would be to use aligned-new, with an alignment
897+ // of max(block_align, obj_align). This would require using aligned-delete in the
898+ // control block, which in turn would either require the control block to always use
899+ // aligned-delete and aligned-new, which could be wasteful, or to know somehow whether
900+ // it has been allocated here or individually.
901+ // Most types will have an alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__, which is
902+ // the alignment guaranteed by the classic operator new, therefore we can safely use
903+ // it and warn the user with a static asset if we can't.
904+ // Going beyond this would require support for custom allocators.
905+
906+ std::byte* buffer = reinterpret_cast <std::byte*>(operator new (obj_offset + obj_size));
891907
892908 try {
893909 // Construct control block first
0 commit comments