diff --git a/stl/inc/functional b/stl/inc/functional index 2caf1cf2b9..69b4d85d54 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1383,20 +1383,19 @@ public: // (The RTTI savings may be significant as with lambdas and binds there may be many distinct callable types. // Here we don't have a distinct wrapper class for each callable type, only distinct functions when needed.) + // _Move and _Destroy are nullptr if trivial. Besides being an optimization, this enables assigning an + // empty function from a DLL that is unloaded later, and then safely moving/destroying that empty function. + // Calls target typename _Invoke_t<_Noexcept>::_Call _Invoke; - // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") + // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable"). + // nullptr if we can trivially move two pointers. void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; - // Destroys data (not resetting its "vtable") + // Destroys data (not resetting its "vtable"). + // nullptr if destruction is a no-op. void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; - static constexpr _Impl_t _Null_move_only_function = { - _Function_not_callable<_Rx, _Types...>, - nullptr, - nullptr, - }; - _Move_only_function_data _Data; _Move_only_function_base() noexcept = default; // leaves fields uninitialized @@ -1407,12 +1406,12 @@ public: } void _Construct_with_null() noexcept { - _Data._Impl = &_Null_move_only_function; + _Data._Impl = nullptr; _Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it } void _Reset_to_null() noexcept { - _Data._Impl = &_Null_move_only_function; + _Data._Impl = nullptr; } template @@ -1426,14 +1425,14 @@ public: } static void _Checked_destroy(_Move_only_function_data& _Data) noexcept { - const auto _Impl = static_cast(_Data._Impl); + const auto _Impl = _Get_impl(_Data); if (_Impl->_Destroy) { _Impl->_Destroy(_Data); } } static void _Checked_move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) noexcept { - const auto _Impl = static_cast(_Src._Impl); + const auto _Impl = _Get_impl(_Src); if (_Impl->_Move) { _Impl->_Move(_Data, _Src); } else { @@ -1447,8 +1446,9 @@ public: // It is more efficient to do the reverse - this way no temporary storage for the old target will be used. // In some cases when some operations are trivial, it can be optimized, // as the order change is unobservable, and everything is noexcept here. - const auto _Other_impl_move = static_cast(_Other._Data._Impl)->_Move; - const auto _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; + const auto _This_impl = _Get_impl(_Data); + const auto _Other_impl_move = _Get_impl(_Other._Data)->_Move; + const auto _This_impl_destroy = _This_impl->_Destroy; if (!_Other_impl_move) { // Move is trivial, destroy first if needed @@ -1462,7 +1462,13 @@ public: } else { // General case involving a temporary _Move_only_function_data _Tmp; - _Checked_move(_Tmp, _Data); + + if (_This_impl->_Move) { + _This_impl->_Move(_Tmp, _Data); + } else { + _Function_move_large(_Tmp, _Data); + } + _Other_impl_move(_Data, _Other._Data); _This_impl_destroy(_Tmp); } @@ -1478,7 +1484,7 @@ public: } _NODISCARD bool _Is_null() const noexcept { - return _Data._Impl == &_Null_move_only_function; + return _Data._Impl == nullptr; } template @@ -1487,7 +1493,18 @@ public: || sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>; _NODISCARD auto _Get_invoke() const noexcept { - return static_cast(_Data._Impl)->_Invoke; + return _Get_impl(_Data)->_Invoke; + } + + _NODISCARD static const _Impl_t* _Get_impl(const _Move_only_function_data& _Data) noexcept { + static constexpr _Impl_t _Null_move_only_function = { + _Function_not_callable<_Rx, _Types...>, + nullptr, + nullptr, + }; + + const auto _Ret = static_cast(_Data._Impl); + return _Ret ? _Ret : &_Null_move_only_function; } template