Update function2

This commit is contained in:
Alex Hultman
2019-10-07 12:03:50 +02:00
parent 6c7844d65f
commit d8b2d3cbb9
+200 -54
View File
@@ -36,11 +36,12 @@
#endif #endif
#endif // FU2_WITH_DISABLED_EXCEPTIONS #endif // FU2_WITH_DISABLED_EXCEPTIONS
// - FU2_HAS_NO_FUNCTIONAL_HEADER // - FU2_HAS_NO_FUNCTIONAL_HEADER
#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) || \ #if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \
!defined(FU2_NO_FUNCTIONAL_HEADER) || \ !defined(FU2_NO_FUNCTIONAL_HEADER) && \
!defined(FU2_HAS_DISABLED_EXCEPTIONS) !defined(FU2_HAS_DISABLED_EXCEPTIONS)
#define FU2_HAS_NO_FUNCTIONAL_HEADER
#include <functional> #include <functional>
#else
#define FU2_HAS_NO_FUNCTIONAL_HEADER
#endif #endif
// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE // - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE) #if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE)
@@ -57,10 +58,53 @@
#endif #endif
#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE #endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
// - FU2_HAS_NO_EMPTY_PROPAGATION
#if defined(FU2_WITH_NO_EMPTY_PROPAGATION)
#define FU2_HAS_NO_EMPTY_PROPAGATION
#endif // FU2_WITH_NO_EMPTY_PROPAGATION
#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) #if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
#include <exception> #include <exception>
#endif #endif
/// Hint for the compiler that this point should be unreachable
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false)
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#elif defined(__has_builtin) && __has_builtin(__builtin_unreachable)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort()
#endif
/// Causes the application to exit abnormally
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __debugbreak()
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#elif defined(__has_builtin) && __has_builtin(__builtin_trap)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0
#endif
#ifndef NDEBUG
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC()
#endif
namespace fu2 { namespace fu2 {
inline namespace abi_400 { inline namespace abi_400 {
namespace detail { namespace detail {
@@ -70,7 +114,7 @@ class function;
template <typename...> template <typename...>
struct identity {}; struct identity {};
// Equivalent to C++17's std::void_t which is targets a bug in GCC, // Equivalent to C++17's std::void_t which targets a bug in GCC,
// that prevents correct SFINAE behavior. // that prevents correct SFINAE behavior.
// See http://stackoverflow.com/questions/35753920 for details. // See http://stackoverflow.com/questions/35753920 for details.
template <typename...> template <typename...>
@@ -79,6 +123,9 @@ struct deduce_to_void : std::common_type<void> {};
template <typename... T> template <typename... T>
using void_t = typename deduce_to_void<T...>::type; using void_t = typename deduce_to_void<T...>::type;
template <typename T>
using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Copy enabler helper class // Copy enabler helper class
template <bool /*Copyable*/> template <bool /*Copyable*/>
struct copyable {}; struct copyable {};
@@ -115,9 +162,17 @@ struct property {
static constexpr auto const is_throwing = Throws; static constexpr auto const is_throwing = Throws;
// Is true when the function throws an exception on empty invocation. // Is true when the function throws an exception on empty invocation.
static constexpr auto const is_strong_exception_guaranteed = Throws; static constexpr auto const is_strong_exception_guaranteed =
HasStrongExceptGuarantee;
}; };
#ifndef NDEBUG
[[noreturn]] inline void unreachable_debug() {
FU2_DETAIL_TRAP();
std::abort();
}
#endif
/// Provides utilities for invocing callable objects /// Provides utilities for invocing callable objects
namespace invocation { namespace invocation {
/// Invokes the given callable object with the given arguments /// Invokes the given callable object with the given arguments
@@ -191,9 +246,9 @@ struct can_invoke<Pointer, identity<T&>,
: std::true_type {}; : std::true_type {};
template <typename Pointer, typename T> template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T&&>, struct can_invoke<Pointer, identity<T&&>,
decltype((void)(std::declval<T&&>().* decltype(
std::declval<Pointer>()))> : std::true_type { (void)(std::declval<T&&>().*std::declval<Pointer>()))>
}; : std::true_type {};
template <typename Pointer, typename T> template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T*>, struct can_invoke<Pointer, identity<T*>,
decltype( decltype(
@@ -334,12 +389,11 @@ struct box_factory<box<IsCopyable, T, Allocator>> {
}; };
/// Creates a box containing the given value and allocator /// Creates a box containing the given value and allocator
template <bool IsCopyable, typename T, template <bool IsCopyable, typename T, typename Allocator>
typename Allocator = std::allocator<std::decay_t<T>>>
auto make_box(std::integral_constant<bool, IsCopyable>, T&& value, auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
Allocator&& allocator = Allocator{}) { Allocator&& allocator) {
return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>{ return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>(
std::forward<T>(value), std::forward<Allocator>(allocator)}; std::forward<T>(value), std::forward<Allocator>(allocator));
} }
template <typename T> template <typename T>
@@ -408,13 +462,13 @@ struct bad_function_call : std::exception {
return "bad function call"; return "bad function call";
} }
}; };
#elif #else
using std::bad_function_call; using std::bad_function_call;
#endif #endif
#endif #endif
#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) \ #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \
F(, , noexcept, , &) \ F(, , noexcept, , &) \
F(const, , noexcept, , &) \ F(const, , noexcept, , &) \
F(, volatile, noexcept, , &) \ F(, volatile, noexcept, , &) \
@@ -427,11 +481,17 @@ using std::bad_function_call;
F(const, , noexcept, &&, &&) \ F(const, , noexcept, &&, &&) \
F(, volatile, noexcept, &&, &&) \ F(, volatile, noexcept, &&, &&) \
F(const, volatile, noexcept, &&, &&) F(const, volatile, noexcept, &&, &&)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \
F(, , noexcept) \
F(const, , noexcept) \
F(, volatile, noexcept) \
F(const, volatile, noexcept)
#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS(F) \ #define FU2_DETAIL_EXPAND_QUALIFIERS(F) \
F(, , , , &) \ F(, , , , &) \
F(const, , , , &) \ F(const, , , , &) \
F(, volatile, , , &) \ F(, volatile, , , &) \
@@ -444,7 +504,13 @@ using std::bad_function_call;
F(const, , , &&, &&) \ F(const, , , &&, &&) \
F(, volatile, , &&, &&) \ F(, volatile, , &&, &&) \
F(const, volatile, , &&, &&) \ F(const, volatile, , &&, &&) \
FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV(F) \
F(, , ) \
F(const, , ) \
F(, volatile, ) \
F(const, volatile, ) \
FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
/// If the function is qualified as noexcept, the call will never throw /// If the function is qualified as noexcept, the call will never throw
template <bool IsNoexcept> template <bool IsNoexcept>
@@ -518,7 +584,7 @@ using is_noexcept_noexcept = std::true_type;
}; \ }; \
}; };
FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT #undef FU2_DEFINE_FUNCTION_TRAIT
/// Deduces to the function pointer to the given signature /// Deduces to the function pointer to the given signature
@@ -669,7 +735,9 @@ class operator_impl;
auto parent = static_cast<Function CONST VOLATILE*>(this); \ auto parent = static_cast<Function CONST VOLATILE*>(this); \
using erasure_t = std::decay_t<decltype(parent->erasure_)>; \ using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
\ \
return erasure_t::template invoke<Index>( \ /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
/* compiler regression of MSVC 16.3.1, see #29 for details. */ \
return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \ static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
std::forward<Args>(args)...); \ std::forward<Args>(args)...); \
} \ } \
@@ -678,7 +746,7 @@ class operator_impl;
typename Ret, typename... Args> \ typename Ret, typename... Args> \
class operator_impl<Index, function<Config, Property>, \ class operator_impl<Index, function<Config, Property>, \
Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \ Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \
: copyable<Config::is_owning || Config::is_copyable> { \ : copyable<!Config::is_owning || Config::is_copyable> { \
\ \
template <std::size_t, typename, typename...> \ template <std::size_t, typename, typename...> \
friend class operator_impl; \ friend class operator_impl; \
@@ -696,13 +764,15 @@ class operator_impl;
static_cast<function<Config, Property> CONST VOLATILE*>(this); \ static_cast<function<Config, Property> CONST VOLATILE*>(this); \
using erasure_t = std::decay_t<decltype(parent->erasure_)>; \ using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
\ \
return erasure_t::template invoke<Index>( \ /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
/* compiler regression of MSVC 16.3.1, see #29 for details. */ \
return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \ static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
std::forward<Args>(args)...); \ std::forward<Args>(args)...); \
} \ } \
}; };
FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT #undef FU2_DEFINE_FUNCTION_TRAIT
} // namespace invocation_table } // namespace invocation_table
@@ -759,7 +829,7 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
// Just swap both pointers if we allocated on the heap // Just swap both pointers if we allocated on the heap
to->ptr_ = from->ptr_; to->ptr_ = from->ptr_;
#ifndef _NDEBUG #ifndef NDEBUG
// We don't need to null the pointer since we know that // We don't need to null the pointer since we know that
// we don't own the data anymore through the vtable // we don't own the data anymore through the vtable
// which is set to empty. // which is set to empty.
@@ -814,9 +884,7 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
} }
} }
// TODO Use an unreachable intrinsic FU2_DETAIL_UNREACHABLE();
assert(false && "Unreachable!");
std::exit(-1);
} }
template <typename Box> template <typename Box>
@@ -865,6 +933,9 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
write_empty(to, true); write_empty(to, true);
break; break;
} }
default: {
FU2_DETAIL_UNREACHABLE();
}
} }
} }
@@ -1045,7 +1116,8 @@ public:
} }
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>> template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
constexpr erasure(T&& callable, Allocator&& allocator = Allocator{}) { constexpr erasure(std::false_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = Allocator{}) {
vtable_t::init(vtable_, vtable_t::init(vtable_,
type_erasure::make_box( type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{}, std::integral_constant<bool, Config::is_copyable>{},
@@ -1053,6 +1125,20 @@ public:
std::forward<Allocator>(allocator)), std::forward<Allocator>(allocator)),
this->opaque_ptr(), capacity()); this->opaque_ptr(), capacity());
} }
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
constexpr erasure(std::true_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = Allocator{}) {
if (bool(callable)) {
vtable_t::init(vtable_,
type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{},
std::forward<T>(callable),
std::forward<Allocator>(allocator)),
this->opaque_ptr(), capacity());
} else {
vtable_.set_empty();
}
}
~erasure() { ~erasure() {
vtable_.weak_destroy(this->opaque_ptr(), capacity()); vtable_.weak_destroy(this->opaque_ptr(), capacity());
@@ -1089,19 +1175,9 @@ public:
return *this; return *this;
} }
template <typename T> template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
constexpr erasure& operator=(T&& callable) { void assign(std::false_type /*use_bool_op*/, T&& callable,
vtable_.weak_destroy(this->opaque_ptr(), capacity()); Allocator&& allocator = {}) {
vtable_t::init(vtable_,
type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{},
std::forward<T>(callable)),
this->opaque_ptr(), capacity());
return *this;
}
template <typename T, typename Allocator>
void assign(T&& callable, Allocator&& allocator) {
vtable_.weak_destroy(this->opaque_ptr(), capacity()); vtable_.weak_destroy(this->opaque_ptr(), capacity());
vtable_t::init(vtable_, vtable_t::init(vtable_,
type_erasure::make_box( type_erasure::make_box(
@@ -1111,6 +1187,17 @@ public:
this->opaque_ptr(), capacity()); this->opaque_ptr(), capacity());
} }
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
void assign(std::true_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = {}) {
if (bool(callable)) {
assign(std::false_type{}, std::forward<T>(callable),
std::forward<Allocator>(allocator));
} else {
operator=(nullptr);
}
}
/// Returns true when the erasure doesn't hold any erased object /// Returns true when the erasure doesn't hold any erased object
constexpr bool empty() const noexcept { constexpr bool empty() const noexcept {
return vtable_.empty(); return vtable_.empty();
@@ -1177,11 +1264,16 @@ public:
template <typename T> template <typename T>
// NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
constexpr erasure(T&& object) constexpr erasure(std::false_type /*use_bool_op*/, T&& object)
: invoke_table_(invoke_table_t::template get_invocation_view_table_of< : invoke_table_(invoke_table_t::template get_invocation_view_table_of<
std::decay_t<T>>()), std::decay_t<T>>()),
view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) { view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
} }
template <typename T>
// NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
constexpr erasure(std::true_type use_bool_op, T&& object) {
this->assign(use_bool_op, std::forward<T>(object));
}
~erasure() = default; ~erasure() = default;
@@ -1211,11 +1303,19 @@ public:
} }
template <typename T> template <typename T>
constexpr erasure& operator=(T&& object) { constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) {
invoke_table_ = invoke_table_t::template get_invocation_view_table_of< invoke_table_ = invoke_table_t::template get_invocation_view_table_of<
std::decay_t<T>>(); std::decay_t<T>>();
view_.ptr_ = address_taker<std::decay_t<T>>::take(std::forward<T>(object)); view_.ptr_ =
return *this; address_taker<std::decay_t<T>>::take(std::forward<T>(callable));
}
template <typename T>
constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) {
if (bool(callable)) {
assign(std::false_type{}, std::forward<T>(callable));
} else {
operator=(nullptr);
}
} }
/// Returns true when the erasure doesn't hold any erased object /// Returns true when the erasure doesn't hold any erased object
@@ -1254,9 +1354,48 @@ struct accepts_all<
void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>> void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
: std::true_type {}; : std::true_type {};
/// Deduces to a true_type if the type T is implementing operator bool()
/// or if the type is convertible to bool directly, this also implements an
/// optimizations for function references `void(&)()` which are can never
/// be null and for such a conversion to bool would never return false.
#if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
template <typename T>
struct use_bool_op : std::false_type {};
#else
template <typename T, typename = void>
struct has_bool_op : std::false_type {};
template <typename T>
struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>>
: std::true_type {
#ifndef NDEBUG
static_assert(!std::is_pointer<T>::value,
"Missing deduction for function pointer!");
#endif
};
template <typename T>
struct use_bool_op : has_bool_op<T> {};
#define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \
template <typename Ret, typename... Args> \
struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT> \
: std::true_type {};
FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT)
#undef FU2_DEFINE_USE_OP_TRAIT
template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...)> : std::false_type {};
#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE)
template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...) noexcept> : std::false_type {};
#endif
#endif // FU2_HAS_NO_EMPTY_PROPAGATION
template <typename Config, typename T> template <typename Config, typename T>
struct assert_wrong_copy_assign { struct assert_wrong_copy_assign {
static_assert(!Config::is_copyable || static_assert(!Config::is_owning || !Config::is_copyable ||
std::is_copy_constructible<std::decay_t<T>>::value, std::is_copy_constructible<std::decay_t<T>>::value,
"Can't wrap a non copyable object into a unique function!"); "Can't wrap a non copyable object into a unique function!");
@@ -1372,7 +1511,8 @@ public:
enable_if_can_accept_all_t<T>* = nullptr, enable_if_can_accept_all_t<T>* = nullptr,
assert_wrong_copy_assign_t<T>* = nullptr, assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr> assert_no_strong_except_guarantee_t<T>* = nullptr>
constexpr function(T&& callable) : erasure_(std::forward<T>(callable)) { constexpr function(T&& callable)
: erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) {
} }
template <typename T, typename Allocator, // template <typename T, typename Allocator, //
enable_if_not_convertible_to_this<T>* = nullptr, enable_if_not_convertible_to_this<T>* = nullptr,
@@ -1381,7 +1521,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr, assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr> assert_no_strong_except_guarantee_t<T>* = nullptr>
constexpr function(T&& callable, Allocator&& allocator) constexpr function(T&& callable, Allocator&& allocator)
: erasure_(std::forward<T>(callable), : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
std::forward<Allocator>(allocator)) { std::forward<Allocator>(allocator)) {
} }
@@ -1418,7 +1558,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr, assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr> assert_no_strong_except_guarantee_t<T>* = nullptr>
function& operator=(T&& callable) { function& operator=(T&& callable) {
erasure_ = std::forward<T>(callable); erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable));
return *this; return *this;
} }
@@ -1445,7 +1585,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr, assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr> assert_no_strong_except_guarantee_t<T>* = nullptr>
void assign(T&& callable, Allocator&& allocator = Allocator{}) { void assign(T&& callable, Allocator&& allocator = Allocator{}) {
erasure_.assign(std::forward<T>(callable), erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
std::forward<Allocator>(allocator)); std::forward<Allocator>(allocator));
} }
@@ -1582,13 +1722,13 @@ using function_view = function_base<false, true, capacity_default, //
/// Exception type that is thrown when invoking empty function objects /// Exception type that is thrown when invoking empty function objects
/// and exception support isn't disabled. /// and exception support isn't disabled.
/// ///
/// Exception suport is enabled if /// Exception support is enabled if
/// the template parameter 'Throwing' is set to true (default). /// the template parameter 'Throwing' is set to true (default).
/// ///
/// This type will default to std::bad_function_call if the /// This type will default to std::bad_function_call if the
/// functional header is used, otherwise the library provides its own type. /// functional header is used, otherwise the library provides its own type.
/// ///
/// You may disable the inclusion of the functionl header /// You may disable the inclusion of the functional header
/// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`. /// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`.
/// ///
using detail::type_erasure::invocation_table::bad_function_call; using detail::type_erasure::invocation_table::bad_function_call;
@@ -1612,7 +1752,13 @@ constexpr auto overload(T&&... callables) {
} }
} // namespace fu2 } // namespace fu2
#undef FU2_EXPAND_QUALIFIERS #undef FU2_DETAIL_EXPAND_QUALIFIERS
#undef FU2_EXPAND_QUALIFIERS_NOEXCEPT #undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT
#undef FU2_DETAIL_EXPAND_CV
#undef FU2_DETAIL_EXPAND_CV_NOEXCEPT
#undef FU2_DETAIL_UNREACHABLE_INTRINSIC
#undef FU2_DETAIL_UNREACHABLE_INTRINSIC
#undef FU2_DETAIL_TRAP
#endif // FU2_INCLUDED_FUNCTION2_HPP_ #endif // FU2_INCLUDED_FUNCTION2_HPP_