зеркало из https://github.com/microsoft/STL.git
P0288R9 move_only_function (#2267)
Co-authored-by: frederick-vs-ja <de34@live.cn> Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Родитель
9265c51c86
Коммит
b33a89ec69
|
@ -1007,7 +1007,7 @@ private:
|
|||
|
||||
template <class _Tx>
|
||||
struct _Get_function_impl {
|
||||
static_assert(_Always_false<_Tx>, "std::function does not accept non-function types as template arguments.");
|
||||
static_assert(_Always_false<_Tx>, "std::function only accepts function types as template arguments.");
|
||||
};
|
||||
|
||||
#define _GET_FUNCTION_IMPL(CALL_OPT, X1, X2, X3) \
|
||||
|
@ -1207,6 +1207,649 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept {
|
|||
}
|
||||
#endif // !_HAS_CXX20
|
||||
|
||||
#if _HAS_CXX23
|
||||
// _Move_only_function_data is defined as an array of pointers.
|
||||
// The first element is always a pointer to _Move_only_function_base::_Impl_t; it emulates a vtable pointer.
|
||||
// The other pointers are used as storage for a small functor;
|
||||
// if the functor does not fit in, the second pointer is the pointer to allocated storage, the rest are unused.
|
||||
union alignas(max_align_t) _Move_only_function_data {
|
||||
void* _Pointers[_Small_object_num_ptrs];
|
||||
const void* _Impl;
|
||||
char _Data; // For aliasing
|
||||
|
||||
template <class _Fn>
|
||||
static constexpr size_t _Buf_offset = alignof(_Fn) <= sizeof(_Impl)
|
||||
? sizeof(_Impl) // Store _Fn immediately after _Impl
|
||||
: alignof(_Fn); // Pad _Fn to next alignment
|
||||
|
||||
template <class _Fn>
|
||||
static constexpr size_t _Buf_size = sizeof(_Pointers) - _Buf_offset<_Fn>;
|
||||
|
||||
template <class _Fn>
|
||||
_NODISCARD void* _Buf_ptr() noexcept {
|
||||
return &_Data + _Buf_offset<_Fn>;
|
||||
}
|
||||
|
||||
template <class _Fn>
|
||||
_NODISCARD _Fn* _Small_fn_ptr() const noexcept {
|
||||
// cast away const to avoid complication of const propagation to here;
|
||||
// const correctness is still enforced by _Move_only_function_call specializations.
|
||||
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
|
||||
}
|
||||
|
||||
template <class _Fn>
|
||||
_NODISCARD _Fn* _Large_fn_ptr() const noexcept {
|
||||
return static_cast<_Fn*>(_Pointers[1]);
|
||||
}
|
||||
|
||||
void _Set_large_fn_ptr(void* const _Value) noexcept {
|
||||
_Pointers[1] = _Value;
|
||||
}
|
||||
};
|
||||
|
||||
// Size of a large function. Treat an empty function as if it has this size.
|
||||
// Treat a small function as if it has this size too if it fits and is trivially copyable.
|
||||
inline constexpr size_t _Minimum_function_size = 2 * sizeof(void*);
|
||||
|
||||
// The below functions are __stdcall as they are called by pointers from _Move_only_function_base::_Impl_t.
|
||||
// (We use explicit __stdcall to make the ABI stable for translation units with different calling convention options.)
|
||||
// Non-template functions are still defined inline, as the compiler may be able to devirtualize some calls.
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
[[noreturn]] _Rx __stdcall _Function_not_callable(const _Move_only_function_data&, _Types&&...) noexcept {
|
||||
_CSTD abort(); // Unlike std::function, move_only_function doesn't throw bad_function_call
|
||||
// (N4901 [func.wrap.move.inv]/2)
|
||||
}
|
||||
|
||||
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
|
||||
_NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
|
||||
return _Invoker_ret<_Rx>::_Call(
|
||||
static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
|
||||
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
|
||||
_NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
|
||||
return _Invoker_ret<_Rx>::_Call(
|
||||
static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
|
||||
template <class _Vt>
|
||||
void __stdcall _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
|
||||
const auto _Src_fn_ptr = _Src._Small_fn_ptr<_Vt>();
|
||||
::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src_fn_ptr));
|
||||
_Src_fn_ptr->~_Vt();
|
||||
_Self._Impl = _Src._Impl;
|
||||
}
|
||||
|
||||
template <size_t _Size>
|
||||
void __stdcall _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
|
||||
_CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data
|
||||
}
|
||||
|
||||
inline void __stdcall _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
|
||||
_CSTD memcpy(&_Self._Data, &_Src._Data, _Minimum_function_size); // Copy Impl* and functor data
|
||||
}
|
||||
|
||||
template <class _Vt>
|
||||
void __stdcall _Function_destroy_small(_Move_only_function_data& _Self) noexcept {
|
||||
_Self._Small_fn_ptr<_Vt>()->~_Vt();
|
||||
}
|
||||
|
||||
inline void __stdcall _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept {
|
||||
::operator delete(_Self._Large_fn_ptr<void>());
|
||||
}
|
||||
|
||||
template <size_t _Align>
|
||||
void __stdcall _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept {
|
||||
::operator delete (_Self._Large_fn_ptr<void>(), align_val_t{_Align});
|
||||
}
|
||||
|
||||
template <class _Vt>
|
||||
void __stdcall _Function_destroy_large(_Move_only_function_data& _Self) noexcept {
|
||||
const auto _Pfn = _Self._Large_fn_ptr<_Vt>();
|
||||
_Pfn->~_Vt();
|
||||
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
::operator delete(static_cast<void*>(_Pfn));
|
||||
} else {
|
||||
::operator delete (static_cast<void*>(_Pfn), align_val_t{alignof(_Vt)});
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Vt>
|
||||
inline constexpr size_t _Function_small_copy_size = // We copy Impl* and the functor data at once
|
||||
_Move_only_function_data::_Buf_offset<_Vt> + // Impl* plus possible alignment
|
||||
(size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}); // size in whole pointers
|
||||
|
||||
template <class _Vt, class... _CTypes>
|
||||
_NODISCARD void* _Function_new_large(_CTypes&&... _Args) {
|
||||
struct _NODISCARD _Guard_type {
|
||||
void* _Ptr;
|
||||
|
||||
~_Guard_type() {
|
||||
// _Ptr is not nullptr only if an exception is thrown as a result of _Vt construction.
|
||||
// Check _Ptr before calling operator delete to save the call in the common case.
|
||||
if (_Ptr) {
|
||||
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
::operator delete(_Ptr);
|
||||
} else {
|
||||
::operator delete (_Ptr, align_val_t{alignof(_Vt)});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void* _Ptr;
|
||||
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
_Ptr = ::operator new(sizeof(_Vt));
|
||||
} else {
|
||||
_Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)});
|
||||
}
|
||||
_Guard_type _Guard{_Ptr};
|
||||
::new (_Ptr) _Vt(_STD forward<_CTypes>(_Args)...);
|
||||
_Guard._Ptr = nullptr;
|
||||
return _Ptr;
|
||||
}
|
||||
|
||||
template <class _Rx, bool _Noexcept, class... _Types>
|
||||
class _Move_only_function_base {
|
||||
public:
|
||||
// TRANSITION, DevCom-1208330: use noexcept(_Noexcept) instead
|
||||
template <bool>
|
||||
struct _Invoke_t {
|
||||
using _Call = _Rx(__stdcall*)(const _Move_only_function_data&, _Types&&...);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct _Invoke_t<true> {
|
||||
using _Call = _Rx(__stdcall*)(const _Move_only_function_data&, _Types&&...) _NOEXCEPT_FNPTR;
|
||||
};
|
||||
|
||||
struct _Impl_t { // A per-callable-type structure acting as a virtual function table.
|
||||
// Using vtable emulations gives more flexibility for optimizations and reduces the amount of RTTI data.
|
||||
// (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.)
|
||||
|
||||
// Calls target
|
||||
typename _Invoke_t<_Noexcept>::_Call _Invoke;
|
||||
// Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable")
|
||||
void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR;
|
||||
// Destroys data (not resetting its "vtable")
|
||||
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
|
||||
|
||||
_Move_only_function_base(_Move_only_function_base&& _Other) noexcept {
|
||||
_Checked_move(_Data, _Other._Data);
|
||||
_Other._Reset_to_null();
|
||||
}
|
||||
|
||||
void _Construct_with_null() noexcept {
|
||||
_Data._Impl = &_Null_move_only_function;
|
||||
_Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it
|
||||
}
|
||||
|
||||
void _Reset_to_null() noexcept {
|
||||
_Data._Impl = &_Null_move_only_function;
|
||||
}
|
||||
|
||||
template <class _Vt, class _VtInvQuals, class... _CTypes>
|
||||
void _Construct_with_fn(_CTypes&&... _Args) {
|
||||
_Data._Impl = _Create_impl_ptr<_Vt, _VtInvQuals>();
|
||||
if constexpr (_Large_function_engaged<_Vt>) {
|
||||
_Data._Set_large_fn_ptr(_Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...));
|
||||
} else {
|
||||
::new (_Data._Buf_ptr<_Vt>()) _Vt(_STD forward<_CTypes>(_Args)...);
|
||||
}
|
||||
}
|
||||
|
||||
static void _Checked_destroy(_Move_only_function_data& _Data) noexcept {
|
||||
const auto _Impl = static_cast<const _Impl_t*>(_Data._Impl);
|
||||
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<const _Impl_t*>(_Src._Impl);
|
||||
if (_Impl->_Move) {
|
||||
_Impl->_Move(_Data, _Src);
|
||||
} else {
|
||||
_Function_move_large(_Data, _Src);
|
||||
}
|
||||
}
|
||||
|
||||
void _Move_assign(_Move_only_function_base&& _Other) noexcept {
|
||||
// As specified in N4901 [func.wrap.move.ctor]/22, we are expected to first move the new target,
|
||||
// then finally destroy the old target.
|
||||
// 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<const _Impl_t*>(_Other._Data._Impl)->_Move;
|
||||
const auto _This_impl_destroy = static_cast<const _Impl_t*>(_Data._Impl)->_Destroy;
|
||||
|
||||
if (!_Other_impl_move) {
|
||||
// Move is trivial, destroy first if needed
|
||||
if (_This_impl_destroy) {
|
||||
_This_impl_destroy(_Data);
|
||||
}
|
||||
_Function_move_large(_Data, _Other._Data);
|
||||
} else if (!_This_impl_destroy) {
|
||||
// Destroy is trivial, just move
|
||||
_Other_impl_move(_Data, _Other._Data);
|
||||
} else {
|
||||
// General case involving a temporary
|
||||
_Move_only_function_data _Tmp;
|
||||
_Checked_move(_Tmp, _Data);
|
||||
_Other_impl_move(_Data, _Other._Data);
|
||||
_This_impl_destroy(_Tmp);
|
||||
}
|
||||
_Other._Reset_to_null();
|
||||
}
|
||||
|
||||
void _Swap(_Move_only_function_base& _Other) noexcept {
|
||||
_Move_only_function_data _Tmp;
|
||||
|
||||
_Checked_move(_Tmp, _Data);
|
||||
_Checked_move(_Data, _Other._Data);
|
||||
_Checked_move(_Other._Data, _Tmp);
|
||||
}
|
||||
|
||||
_NODISCARD bool _Is_null() const noexcept {
|
||||
return _Data._Impl == &_Null_move_only_function;
|
||||
}
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Large_function_engaged =
|
||||
alignof(_Vt) > alignof(max_align_t)
|
||||
|| sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>;
|
||||
|
||||
_NODISCARD auto _Get_invoke() const noexcept {
|
||||
return static_cast<const _Impl_t*>(_Data._Impl)->_Invoke;
|
||||
}
|
||||
|
||||
template <class _Vt, class _VtInvQuals>
|
||||
_NODISCARD static constexpr _Impl_t _Create_impl() noexcept {
|
||||
_Impl_t _Impl{};
|
||||
if constexpr (_Large_function_engaged<_Vt>) {
|
||||
_Impl._Invoke = _Function_inv_large<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
|
||||
_Impl._Move = nullptr;
|
||||
|
||||
if constexpr (is_trivially_destructible_v<_Vt>) {
|
||||
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
_Impl._Destroy = _Function_deallocate_large_default_aligned;
|
||||
} else {
|
||||
_Impl._Destroy = _Function_deallocate_large_overaligned<alignof(_Vt)>;
|
||||
}
|
||||
} else {
|
||||
_Impl._Destroy = _Function_destroy_large<_Vt>;
|
||||
}
|
||||
} else {
|
||||
_Impl._Invoke = _Function_inv_small<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
|
||||
|
||||
if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) {
|
||||
if constexpr ((_Function_small_copy_size<_Vt>) > _Minimum_function_size) {
|
||||
_Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>>;
|
||||
} else {
|
||||
_Impl._Move = nullptr;
|
||||
}
|
||||
} else {
|
||||
_Impl._Move = _Function_move_small<_Vt>;
|
||||
}
|
||||
|
||||
if constexpr (is_trivially_destructible_v<_Vt>) {
|
||||
_Impl._Destroy = nullptr;
|
||||
} else {
|
||||
_Impl._Destroy = _Function_destroy_small<_Vt>;
|
||||
}
|
||||
}
|
||||
return _Impl;
|
||||
}
|
||||
|
||||
template <class _Vt, class _VtInvQuals>
|
||||
_NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept {
|
||||
static constexpr _Impl_t _Impl = _Create_impl<_Vt, _VtInvQuals>();
|
||||
return &_Impl;
|
||||
}
|
||||
};
|
||||
|
||||
template <class... _Signature>
|
||||
class _Move_only_function_call {
|
||||
static_assert((_Always_false<_Signature> || ...),
|
||||
"std::move_only_function only accepts function types as template arguments, "
|
||||
"with possibly const/ref/noexcept qualifiers.");
|
||||
|
||||
static_assert(sizeof...(_Signature) > 0,
|
||||
"Unlike std::function, std::move_only_function does not define class template argument deduction guides.");
|
||||
};
|
||||
|
||||
// A script to generate the specializations is at
|
||||
// /tools/move_only_function_specializations/move_only_function_specializations.py
|
||||
// (Avoiding C++ preprocessor for better IDE navigation and debugging experience)
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from =
|
||||
is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...)&> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) & {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) &&> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) && {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from =
|
||||
is_invocable_r_v<_Rx, const _Vt, _Types...>&& is_invocable_r_v<_Rx, const _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const&> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const& {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const&&> : public _Move_only_function_base<_Rx, false, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const&& {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from =
|
||||
is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...)& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) & noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...)&& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = _Vt&&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) && noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from =
|
||||
is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const& noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) const&& noexcept>
|
||||
: public _Move_only_function_base<_Rx, true, _Types...> {
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = const _Vt&&;
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>;
|
||||
|
||||
_Rx operator()(_Types... _Args) const&& noexcept {
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}
|
||||
};
|
||||
#endif // __cpp_noexcept_function_type
|
||||
|
||||
template <class... _Signature>
|
||||
class move_only_function : private _Move_only_function_call<_Signature...> {
|
||||
private:
|
||||
using _Call = _Move_only_function_call<_Signature...>;
|
||||
|
||||
// clang-format off
|
||||
template <class _Fn>
|
||||
static constexpr bool _Enable_one_arg_constructor =
|
||||
!is_same_v<remove_cvref_t<_Fn>, move_only_function>
|
||||
&& !_Is_specialization_v<remove_cvref_t<_Fn>, in_place_type_t>
|
||||
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
|
||||
|
||||
template <class _Fn, class... _CTypes>
|
||||
static constexpr bool _Enable_in_place_constructor =
|
||||
is_constructible_v<decay_t<_Fn>, _CTypes...>
|
||||
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
|
||||
|
||||
template <class _Fn, class _Ux, class... _CTypes>
|
||||
static constexpr bool _Enable_in_place_list_constructor =
|
||||
is_constructible_v<decay_t<_Fn>, initializer_list<_Ux>&, _CTypes...>
|
||||
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
|
||||
// clang-format on
|
||||
public:
|
||||
using typename _Call::result_type;
|
||||
|
||||
move_only_function() noexcept {
|
||||
this->_Construct_with_null();
|
||||
}
|
||||
|
||||
move_only_function(nullptr_t) noexcept {
|
||||
this->_Construct_with_null();
|
||||
}
|
||||
|
||||
move_only_function(move_only_function&&) noexcept = default;
|
||||
|
||||
template <class _Fn, enable_if_t<_Enable_one_arg_constructor<_Fn>, int> = 0>
|
||||
move_only_function(_Fn&& _Callable) {
|
||||
using _Vt = decay_t<_Fn>;
|
||||
static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. "
|
||||
"(N4901 [func.wrap.move.ctor]/6)");
|
||||
|
||||
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> || _Is_specialization_v<_Vt, move_only_function>) {
|
||||
if (_Callable == nullptr) {
|
||||
this->_Construct_with_null();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
|
||||
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
|
||||
}
|
||||
|
||||
template <class _Fn, class... _CTypes, enable_if_t<_Enable_in_place_constructor<_Fn, _CTypes...>, int> = 0>
|
||||
explicit move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) {
|
||||
using _Vt = decay_t<_Fn>;
|
||||
static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4901 [func.wrap.move.ctor]/12)");
|
||||
|
||||
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
|
||||
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_CTypes>(_Args)...);
|
||||
}
|
||||
|
||||
template <class _Fn, class _Ux, class... _CTypes,
|
||||
enable_if_t<_Enable_in_place_list_constructor<_Fn, _Ux, _CTypes...>, int> = 0>
|
||||
explicit move_only_function(in_place_type_t<_Fn>, initializer_list<_Ux> _Li, _CTypes&&... _Args) {
|
||||
using _Vt = decay_t<_Fn>;
|
||||
static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4901 [func.wrap.move.ctor]/18)");
|
||||
|
||||
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
|
||||
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_Li, _STD forward<_CTypes>(_Args)...);
|
||||
}
|
||||
|
||||
~move_only_function() {
|
||||
// Do cleanup in this class destructor rather than base,
|
||||
// so that if object construction throws, the unnecessary cleanup isn't called.
|
||||
this->_Checked_destroy(this->_Data);
|
||||
}
|
||||
|
||||
move_only_function& operator=(nullptr_t) noexcept {
|
||||
this->_Checked_destroy(this->_Data);
|
||||
this->_Reset_to_null();
|
||||
return *this;
|
||||
}
|
||||
|
||||
move_only_function& operator=(move_only_function&& _Other) {
|
||||
if (this != _STD addressof(_Other)) {
|
||||
this->_Move_assign(_STD move(_Other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Fn, enable_if_t<is_constructible_v<move_only_function, _Fn>, int> = 0>
|
||||
move_only_function& operator=(_Fn&& _Callable) {
|
||||
this->_Move_assign(move_only_function{_STD forward<_Fn>(_Callable)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
_NODISCARD explicit operator bool() const noexcept {
|
||||
return !this->_Is_null();
|
||||
}
|
||||
|
||||
using _Call::operator();
|
||||
|
||||
void swap(move_only_function& _Other) noexcept {
|
||||
this->_Swap(_Other);
|
||||
}
|
||||
|
||||
friend void swap(move_only_function& _Fn1, move_only_function& _Fn2) noexcept {
|
||||
_Fn1._Swap(_Fn2);
|
||||
}
|
||||
|
||||
_NODISCARD friend bool operator==(const move_only_function& _This, nullptr_t) noexcept {
|
||||
return _This._Is_null();
|
||||
}
|
||||
};
|
||||
#endif // _HAS_CXX23
|
||||
|
||||
template <int _Nx>
|
||||
struct _Ph { // placeholder
|
||||
static_assert(_Nx > 0, "invalid placeholder index");
|
||||
|
|
|
@ -279,6 +279,7 @@
|
|||
// Other C++20 deprecation warnings
|
||||
|
||||
// _HAS_CXX23 directly controls:
|
||||
// P0288R9 move_only_function
|
||||
// P0401R6 Providing Size Feedback In The Allocator Interface
|
||||
// P0448R4 <spanstream>
|
||||
// P0943R6 Supporting C Atomics In C++
|
||||
|
@ -1381,9 +1382,10 @@
|
|||
#define __cpp_lib_allocate_at_least 202106L
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
#define __cpp_lib_byteswap 202110L
|
||||
#define __cpp_lib_invoke_r 202106L
|
||||
#define __cpp_lib_is_scoped_enum 202011L
|
||||
#define __cpp_lib_byteswap 202110L
|
||||
#define __cpp_lib_invoke_r 202106L
|
||||
#define __cpp_lib_is_scoped_enum 202011L
|
||||
#define __cpp_lib_move_only_function 202110L
|
||||
|
||||
#ifdef __cpp_lib_concepts
|
||||
#define __cpp_lib_out_ptr 202106L
|
||||
|
|
|
@ -244,6 +244,7 @@ tests\P0220R1_polymorphic_memory_resources
|
|||
tests\P0220R1_sample
|
||||
tests\P0220R1_searchers
|
||||
tests\P0220R1_string_view
|
||||
tests\P0288R9_move_only_function
|
||||
tests\P0325R4_to_array
|
||||
tests\P0339R6_polymorphic_allocator
|
||||
tests\P0355R7_calendars_and_time_zones_clocks
|
||||
|
|
|
@ -43,16 +43,15 @@ struct not_overaligned_t {
|
|||
|
||||
static_assert(alignof(overaligned_t) > alignof(std::max_align_t), "overaligned_t is not overaligned");
|
||||
|
||||
using function_t = std::function<void(const void* storage, std::size_t storage_size)>;
|
||||
template <class function_t>
|
||||
void test() {
|
||||
struct functions_t {
|
||||
function_t first{overaligned_t{}};
|
||||
char smallest_pad;
|
||||
function_t second{overaligned_t{}};
|
||||
function_t third{overaligned_t{}};
|
||||
};
|
||||
|
||||
struct functions_t {
|
||||
function_t first{overaligned_t{}};
|
||||
char smallest_pad;
|
||||
function_t second{overaligned_t{}};
|
||||
function_t third{overaligned_t{}};
|
||||
};
|
||||
|
||||
int main() {
|
||||
functions_t functions;
|
||||
functions.first(&functions.first, sizeof(functions.first));
|
||||
functions.second(&functions.second, sizeof(functions.second));
|
||||
|
@ -60,6 +59,12 @@ int main() {
|
|||
|
||||
function_t sfo{not_overaligned_t{}};
|
||||
sfo(&sfo, sizeof(sfo));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
test<std::function<void(const void* storage, std::size_t storage_size)>>();
|
||||
#ifdef __cpp_lib_move_only_function
|
||||
test<std::move_only_function<void(const void* storage, std::size_t storage_size)>>();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\usual_latest_matrix.lst
|
|
@ -0,0 +1,563 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
constexpr auto large_function_size = 100;
|
||||
|
||||
struct pass_this_by_ref {
|
||||
int v;
|
||||
|
||||
pass_this_by_ref(int v_) : v(v_) {}
|
||||
|
||||
pass_this_by_ref(const pass_this_by_ref&) {
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
struct counter {
|
||||
static int inst;
|
||||
static int copies;
|
||||
static int moves;
|
||||
|
||||
counter() {
|
||||
++inst;
|
||||
}
|
||||
|
||||
counter(const counter&) {
|
||||
++inst;
|
||||
++copies;
|
||||
}
|
||||
|
||||
counter(counter&&) noexcept {
|
||||
++inst;
|
||||
++moves;
|
||||
}
|
||||
|
||||
~counter() {
|
||||
--inst;
|
||||
}
|
||||
};
|
||||
|
||||
int counter::inst = 0;
|
||||
int counter::copies = 0;
|
||||
int counter::moves = 0;
|
||||
|
||||
struct small_callable : counter {
|
||||
int operator()(int a, pass_this_by_ref& b) {
|
||||
assert(a == 23);
|
||||
assert(b.v == 63);
|
||||
return 38;
|
||||
}
|
||||
|
||||
void* operator new(size_t) = delete;
|
||||
void operator delete(void*) = delete;
|
||||
|
||||
small_callable() = default;
|
||||
|
||||
small_callable(const small_callable&) = default;
|
||||
|
||||
small_callable(small_callable&&) noexcept = default;
|
||||
};
|
||||
|
||||
struct large_callable : counter {
|
||||
char data[large_function_size] = {};
|
||||
|
||||
int operator()(int a, pass_this_by_ref& b) {
|
||||
assert(a == 23);
|
||||
assert(b.v == 63);
|
||||
return 39;
|
||||
}
|
||||
|
||||
void* operator new(size_t) = delete;
|
||||
void operator delete(void*) = delete;
|
||||
|
||||
large_callable() = default;
|
||||
|
||||
large_callable(const large_callable&) = default;
|
||||
|
||||
large_callable(large_callable&&) noexcept = default;
|
||||
};
|
||||
|
||||
struct odd_cc_callable : counter {
|
||||
int __fastcall operator()(int a, pass_this_by_ref& b) {
|
||||
assert(a == 23);
|
||||
assert(b.v == 63);
|
||||
return 40;
|
||||
}
|
||||
|
||||
odd_cc_callable() = default;
|
||||
|
||||
odd_cc_callable(const odd_cc_callable&) = default;
|
||||
odd_cc_callable(odd_cc_callable&&) noexcept = default;
|
||||
};
|
||||
|
||||
struct large_implicit_ptr_callable : counter {
|
||||
char data[large_function_size] = {};
|
||||
|
||||
using pfn = int (*)(int a, pass_this_by_ref& b);
|
||||
|
||||
operator pfn() {
|
||||
return [](int a, pass_this_by_ref& b) {
|
||||
assert(a == 23);
|
||||
assert(b.v == 63);
|
||||
return 41;
|
||||
};
|
||||
}
|
||||
|
||||
large_implicit_ptr_callable() = default;
|
||||
|
||||
large_implicit_ptr_callable(const large_implicit_ptr_callable&) = default;
|
||||
large_implicit_ptr_callable(large_implicit_ptr_callable&&) noexcept = default;
|
||||
};
|
||||
|
||||
int __fastcall plain_callable(int a, pass_this_by_ref& b) {
|
||||
assert(a == 23);
|
||||
assert(b.v == 63);
|
||||
return 42;
|
||||
}
|
||||
|
||||
using test_function_t = move_only_function<int(int, pass_this_by_ref&)>;
|
||||
|
||||
template <class F, class... Args>
|
||||
void test_construct_impl(int expect, Args... args) {
|
||||
{
|
||||
pass_this_by_ref x{63};
|
||||
|
||||
test_function_t constructed_directly(F{args...});
|
||||
|
||||
assert(constructed_directly(23, x) == expect);
|
||||
|
||||
assert(constructed_directly);
|
||||
assert(constructed_directly != nullptr);
|
||||
|
||||
test_function_t move_constructed = move(constructed_directly);
|
||||
|
||||
assert(move_constructed(23, x) == expect);
|
||||
|
||||
if constexpr (is_class_v<F>) {
|
||||
assert(counter::copies == 0);
|
||||
}
|
||||
|
||||
F v{args...};
|
||||
test_function_t constructed_lvalue(v);
|
||||
if constexpr (is_class_v<F>) {
|
||||
assert(counter::copies == 1);
|
||||
counter::copies = 0;
|
||||
}
|
||||
|
||||
if constexpr (is_class_v<F>) {
|
||||
counter::copies = 0;
|
||||
counter::moves = 0;
|
||||
}
|
||||
test_function_t constructed_in_place(in_place_type<F>, args...);
|
||||
assert(constructed_in_place(23, x) == expect);
|
||||
if constexpr (is_class_v<F>) {
|
||||
assert(counter::copies == 0);
|
||||
assert(counter::moves == 0);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (is_class_v<F>) {
|
||||
assert(counter::inst == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void test_assign() {
|
||||
pass_this_by_ref x{63};
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{large_callable{}};
|
||||
f2 = move(f1);
|
||||
assert(f2(23, x) == 38);
|
||||
f1 = large_callable{};
|
||||
assert(f1(23, x) == 39);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{large_callable{}};
|
||||
test_function_t f2{small_callable{}};
|
||||
f2 = move(f1);
|
||||
assert(f2(23, x) == 39);
|
||||
f1 = small_callable{};
|
||||
assert(f1(23, x) == 38);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{odd_cc_callable{}};
|
||||
f2 = move(f1);
|
||||
assert(f2(23, x) == 38);
|
||||
f1 = odd_cc_callable{};
|
||||
assert(f1(23, x) == 40);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{large_callable{}};
|
||||
test_function_t f2{large_implicit_ptr_callable{}};
|
||||
f2 = move(f1);
|
||||
assert(f2(23, x) == 39);
|
||||
f1 = large_implicit_ptr_callable{};
|
||||
assert(f1(23, x) == 41);
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-move"
|
||||
#endif // __clang__
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{large_callable{}};
|
||||
f1 = move(f1); // deliberate self-move as a test case
|
||||
#pragma warning(suppress : 26800) // use a moved-from object
|
||||
assert(f1(23, x) == 38);
|
||||
f2 = move(f2); // deliberate self-move as a test case
|
||||
#pragma warning(suppress : 26800) // use a moved-from object
|
||||
assert(f2(23, x) == 39);
|
||||
}
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif // __clang__
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{large_callable{}};
|
||||
test_function_t f3{small_callable{}};
|
||||
test_function_t f4{large_callable{}};
|
||||
test_function_t f5{nullptr};
|
||||
assert(f1);
|
||||
assert(f2);
|
||||
assert(f3);
|
||||
assert(f4);
|
||||
assert(!f5);
|
||||
f1 = nullptr;
|
||||
f2 = nullptr;
|
||||
f3 = test_function_t{nullptr};
|
||||
f4 = test_function_t{nullptr};
|
||||
assert(!f1);
|
||||
assert(!f2);
|
||||
assert(!f3);
|
||||
assert(!f4);
|
||||
}
|
||||
}
|
||||
|
||||
void test_swap() {
|
||||
pass_this_by_ref x{63};
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{large_callable{}};
|
||||
swap(f1, f2);
|
||||
assert(f2(23, x) == 38);
|
||||
assert(f1(23, x) == 39);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{odd_cc_callable{}};
|
||||
f1.swap(f2);
|
||||
assert(f2(23, x) == 38);
|
||||
assert(f1(23, x) == 40);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{large_callable{}};
|
||||
test_function_t f2{large_implicit_ptr_callable{}};
|
||||
f2.swap(f1);
|
||||
assert(f2(23, x) == 39);
|
||||
assert(f1(23, x) == 41);
|
||||
}
|
||||
|
||||
{
|
||||
test_function_t f1{small_callable{}};
|
||||
test_function_t f2{large_callable{}};
|
||||
swap(f1, f1);
|
||||
f2.swap(f2);
|
||||
assert(f1(23, x) == 38);
|
||||
assert(f2(23, x) == 39);
|
||||
}
|
||||
}
|
||||
|
||||
void test_empty() {
|
||||
test_function_t no_callable;
|
||||
assert(!no_callable);
|
||||
assert(no_callable == nullptr);
|
||||
assert(nullptr == no_callable);
|
||||
|
||||
test_function_t no_callable_moved = move(no_callable);
|
||||
#pragma warning(suppress : 26800) // use a moved-from object
|
||||
assert(!no_callable);
|
||||
assert(no_callable == nullptr);
|
||||
assert(!no_callable_moved);
|
||||
assert(no_callable_moved == nullptr);
|
||||
}
|
||||
|
||||
void test_ptr() {
|
||||
struct s_t {
|
||||
int f(int p) {
|
||||
return p + 2;
|
||||
}
|
||||
|
||||
int j = 6;
|
||||
|
||||
static int g(int z) {
|
||||
return z - 3;
|
||||
}
|
||||
};
|
||||
|
||||
move_only_function<int(s_t*, int)> mem_fun_ptr(&s_t::f);
|
||||
move_only_function<int(s_t*)> mem_ptr(&s_t::j);
|
||||
move_only_function<int(int)> fun_ptr(&s_t::g);
|
||||
|
||||
s_t s;
|
||||
assert(mem_fun_ptr);
|
||||
assert(mem_fun_ptr(&s, 3) == 5);
|
||||
assert(mem_ptr);
|
||||
assert(mem_ptr(&s) == 6);
|
||||
assert(fun_ptr);
|
||||
assert(fun_ptr(34) == 31);
|
||||
|
||||
move_only_function<int(s_t*, int)> mem_fun_ptr_n(static_cast<decltype(&s_t::f)>(nullptr));
|
||||
move_only_function<int(s_t*)> mem_ptr_n(static_cast<decltype(&s_t::j)>(nullptr));
|
||||
move_only_function<int(int)> fun_ptr_n(static_cast<decltype(&s_t::g)>(nullptr));
|
||||
|
||||
assert(!mem_fun_ptr_n);
|
||||
assert(!mem_ptr_n);
|
||||
assert(!fun_ptr_n);
|
||||
}
|
||||
|
||||
void test_inner() {
|
||||
move_only_function<short(long, long)> f1(nullptr);
|
||||
move_only_function<int(int, int)> f2 = move(f1);
|
||||
assert(!f2);
|
||||
#pragma warning(suppress : 26800) // use a moved-from object
|
||||
f2 = move(f1);
|
||||
assert(!f1);
|
||||
}
|
||||
|
||||
|
||||
void test_inplace_list() {
|
||||
struct in_place_list_constructible {
|
||||
in_place_list_constructible(initializer_list<int> li) {
|
||||
int x = 0;
|
||||
for (int i : li) {
|
||||
++x;
|
||||
assert(x == i);
|
||||
}
|
||||
}
|
||||
|
||||
in_place_list_constructible(initializer_list<int> li, const char*) {
|
||||
int x = 0;
|
||||
for (int i : li) {
|
||||
--x;
|
||||
assert(x == i);
|
||||
}
|
||||
}
|
||||
|
||||
in_place_list_constructible(const in_place_list_constructible&) = delete;
|
||||
in_place_list_constructible& operator=(const in_place_list_constructible&) = delete;
|
||||
|
||||
int operator()(int i) {
|
||||
return i - 1;
|
||||
}
|
||||
};
|
||||
|
||||
move_only_function<int(int)> f1(in_place_type<in_place_list_constructible>, {1, 2, 3, 4, 5});
|
||||
assert(f1(5) == 4);
|
||||
|
||||
move_only_function<int(int)> f2(in_place_type<in_place_list_constructible>, {-1, -2, -3, -4, -5}, "fox");
|
||||
assert(f2(8) == 7);
|
||||
}
|
||||
|
||||
|
||||
template <bool Nx>
|
||||
struct test_noexcept_t {
|
||||
int operator()() noexcept(Nx) {
|
||||
return 888;
|
||||
}
|
||||
};
|
||||
|
||||
void test_noexcept() {
|
||||
using f_x = move_only_function<int()>;
|
||||
using f_nx = move_only_function<int() noexcept>;
|
||||
|
||||
static_assert(!noexcept(declval<f_x>()()));
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
static_assert(noexcept(declval<f_nx>()()));
|
||||
#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv
|
||||
static_assert(!noexcept(declval<f_nx>()()));
|
||||
#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^
|
||||
|
||||
static_assert(is_constructible_v<f_x, test_noexcept_t<false>>);
|
||||
assert(f_x(test_noexcept_t<false>{})() == 888);
|
||||
|
||||
static_assert(is_constructible_v<f_x, test_noexcept_t<true>>);
|
||||
assert(f_x(test_noexcept_t<true>{})() == 888);
|
||||
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
static_assert(!is_constructible_v<f_nx, test_noexcept_t<false>>);
|
||||
#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv
|
||||
static_assert(is_constructible_v<f_nx, test_noexcept_t<false>>);
|
||||
assert(f_nx(test_noexcept_t<false>{})() == 888);
|
||||
#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^
|
||||
|
||||
static_assert(is_constructible_v<f_nx, test_noexcept_t<true>>);
|
||||
assert(f_nx(test_noexcept_t<true>{})() == 888);
|
||||
}
|
||||
|
||||
template <bool>
|
||||
struct test_const_t {
|
||||
int operator()() {
|
||||
return 456;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct test_const_t<true> {
|
||||
int operator()() const {
|
||||
return 456;
|
||||
}
|
||||
};
|
||||
|
||||
void test_const() {
|
||||
using f_c = move_only_function<int() const>;
|
||||
using f_nc = move_only_function<int()>;
|
||||
|
||||
static_assert(is_constructible_v<f_nc, test_const_t<false>>);
|
||||
f_nc f1(test_const_t<false>{});
|
||||
assert(f1() == 456);
|
||||
|
||||
static_assert(is_constructible_v<f_nc, test_const_t<true>>);
|
||||
f_nc f2(test_const_t<true>{});
|
||||
assert(f2() == 456);
|
||||
|
||||
static_assert(!is_constructible_v<f_c, test_const_t<false>>);
|
||||
|
||||
static_assert(is_constructible_v<f_c, test_const_t<true>>);
|
||||
f_c f3(test_const_t<true>{});
|
||||
assert(f3() == 456);
|
||||
const f_c f4(test_const_t<true>{});
|
||||
assert(f4() == 456);
|
||||
}
|
||||
|
||||
|
||||
void test_qual() {
|
||||
move_only_function<int(int)> f1([](auto i) { return i + 1; });
|
||||
assert(f1(1) == 2);
|
||||
move_only_function<int(int)&> f2([](auto i) { return i + 1; });
|
||||
assert(f2(2) == 3);
|
||||
move_only_function<int(int) &&> f3([](auto i) { return i + 1; });
|
||||
assert(move(f3)(3) == 4);
|
||||
|
||||
move_only_function<int(int) const> f1c([](auto i) { return i + 1; });
|
||||
assert(f1c(4) == 5);
|
||||
move_only_function<int(int) const&> f2c([](auto i) { return i + 1; });
|
||||
assert(f2c(5) == 6);
|
||||
move_only_function<int(int) const&&> f3c([](auto i) { return i + 1; });
|
||||
assert(move(f3c)(6) == 7);
|
||||
|
||||
move_only_function<int(int) noexcept> f1_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(f1_nx(1) == 2);
|
||||
move_only_function<int(int)& noexcept> f2_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(f2_nx(2) == 3);
|
||||
move_only_function<int(int)&& noexcept> f3_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(move(f3_nx)(3) == 4);
|
||||
|
||||
move_only_function<int(int) const noexcept> f1c_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(f1c_nx(4) == 5);
|
||||
move_only_function<int(int) const& noexcept> f2c_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(f2c_nx(5) == 6);
|
||||
move_only_function<int(int) const&& noexcept> f3c_nx([](auto i) noexcept { return i + 1; });
|
||||
assert(move(f3c_nx)(6) == 7);
|
||||
}
|
||||
|
||||
static_assert(is_same_v<move_only_function<void()>::result_type, void>);
|
||||
static_assert(is_same_v<move_only_function<short(long&) &>::result_type, short>);
|
||||
static_assert(is_same_v<move_only_function<int(char*) &&>::result_type, int>);
|
||||
static_assert(is_same_v<move_only_function<void() const>::result_type, void>);
|
||||
static_assert(is_same_v<move_only_function<short(long&) const&>::result_type, short>);
|
||||
static_assert(is_same_v<move_only_function<int(char*) const&&>::result_type, int>);
|
||||
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
static_assert(is_same_v<move_only_function<void() noexcept>::result_type, void>);
|
||||
static_assert(is_same_v<move_only_function<short(long&) & noexcept>::result_type, short>);
|
||||
static_assert(is_same_v<move_only_function<int(char*) && noexcept>::result_type, int>);
|
||||
static_assert(is_same_v<move_only_function<void() const noexcept>::result_type, void>);
|
||||
static_assert(is_same_v<move_only_function<short(long&) const& noexcept>::result_type, short>);
|
||||
static_assert(is_same_v<move_only_function<int(char*) const&& noexcept>::result_type, int>);
|
||||
#endif // ^^^ defined(__cpp_noexcept_function_type) ^^^
|
||||
|
||||
bool fail_allocations = false;
|
||||
|
||||
#pragma warning(suppress : 28251) // Inconsistent annotation for 'new': this instance has no annotations.
|
||||
void* operator new(size_t size) {
|
||||
if (fail_allocations) {
|
||||
throw bad_alloc{};
|
||||
}
|
||||
void* result = size > 0 ? malloc(size) : malloc(1);
|
||||
if (!result) {
|
||||
throw bad_alloc{};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void operator delete(void* p) noexcept {
|
||||
free(p);
|
||||
}
|
||||
|
||||
void test_except() {
|
||||
struct throwing {
|
||||
throwing() = default;
|
||||
throwing(throwing&&) { // not noexcept to avoid small functor optimization
|
||||
throw runtime_error{"boo"};
|
||||
}
|
||||
void operator()() {}
|
||||
};
|
||||
|
||||
struct not_throwing {
|
||||
not_throwing() = default;
|
||||
not_throwing(not_throwing&&) {} // not noexcept to avoid small functor optimization
|
||||
void operator()() {}
|
||||
};
|
||||
|
||||
try {
|
||||
move_only_function<void()> f{throwing{}};
|
||||
assert(false); // unreachable
|
||||
} catch (runtime_error&) {
|
||||
}
|
||||
|
||||
try {
|
||||
fail_allocations = true;
|
||||
move_only_function<void()> f{not_throwing{}};
|
||||
assert(false); // unreachable
|
||||
} catch (bad_alloc&) {
|
||||
fail_allocations = false;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_construct_impl<small_callable>(38);
|
||||
test_construct_impl<large_callable>(39);
|
||||
test_construct_impl<odd_cc_callable>(40);
|
||||
test_construct_impl<large_implicit_ptr_callable>(41);
|
||||
test_construct_impl<decltype(&plain_callable)>(42, plain_callable);
|
||||
test_assign();
|
||||
test_swap();
|
||||
test_empty();
|
||||
test_ptr();
|
||||
test_inner();
|
||||
test_inplace_list();
|
||||
test_noexcept();
|
||||
test_const();
|
||||
test_qual();
|
||||
test_except();
|
||||
}
|
|
@ -1178,6 +1178,20 @@ STATIC_ASSERT(__cpp_lib_memory_resource == 201603L);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if _HAS_CXX23
|
||||
#ifndef __cpp_lib_move_only_function
|
||||
#error __cpp_lib_move_only_function is not defined
|
||||
#elif __cpp_lib_move_only_function != 202110L
|
||||
#error __cpp_lib_move_only_function is not 202110L
|
||||
#else
|
||||
STATIC_ASSERT(__cpp_lib_move_only_function == 202110L);
|
||||
#endif
|
||||
#else
|
||||
#ifdef __cpp_lib_move_only_function
|
||||
#error __cpp_lib_move_only_function is defined
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if _HAS_CXX17
|
||||
#ifndef __cpp_lib_node_extract
|
||||
#error __cpp_lib_node_extract is not defined
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
template = """template <class _Rx, class... _Types>
|
||||
class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}>
|
||||
: public _Move_only_function_base<_Rx, {noex_val}, _Types...> {{
|
||||
public:
|
||||
using result_type = _Rx;
|
||||
|
||||
template <class _Vt>
|
||||
using _VtInvQuals = {cv} _Vt {ref_inv};
|
||||
|
||||
template <class _Vt>
|
||||
static constexpr bool _Is_callable_from = {callable};
|
||||
|
||||
_Rx operator()(_Types... _Args) {cv} {ref} {noex} {{
|
||||
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
|
||||
}}
|
||||
}};"""
|
||||
|
||||
def ref_permutations(cv, noex, noex_val, trait):
|
||||
callable = "{trait}<_Rx, {cv} _Vt, _Types...> && {trait}<_Rx, {cv} _Vt&, _Types...>".format(trait = trait, cv = cv)
|
||||
print(template.format(cv = cv, ref = "", ref_inv = "&", noex = noex, noex_val = noex_val, callable = callable))
|
||||
print("")
|
||||
callable = "{trait}<_Rx, {cv} _Vt&, _Types...>".format(trait = trait, cv = cv)
|
||||
print(template.format(cv = cv, ref = "&", ref_inv = "&", noex = noex, noex_val = noex_val, callable = callable))
|
||||
print("")
|
||||
callable = "{trait}<_Rx, {cv} _Vt, _Types...>".format(trait = trait, cv = cv)
|
||||
print(template.format(cv = cv, ref = "&&", ref_inv = "&&", noex = noex, noex_val = noex_val, callable = callable))
|
||||
|
||||
def cvref_permutations(noex, noex_val, trait):
|
||||
ref_permutations("", noex, noex_val, trait)
|
||||
print("")
|
||||
ref_permutations("const", noex, noex_val, trait)
|
||||
|
||||
cvref_permutations("", "false", "is_invocable_r_v")
|
||||
print("")
|
||||
print("#ifdef __cpp_noexcept_function_type")
|
||||
cvref_permutations("noexcept", "true", "is_nothrow_invocable_r_v")
|
||||
print("#endif // __cpp_noexcept_function_type")
|
Загрузка…
Ссылка в новой задаче