Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,8 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) {
template <class _Rx, bool _Noexcept, class... _Types>
class _Function_base {
public:
using result_type = _Rx;
using result_type = _Rx;
using _Signature_without_cv_ref_noex = _Rx(_Types...);

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.
Expand Down Expand Up @@ -2060,6 +2061,19 @@ private:
is_constructible_v<decay_t<_Fn>, initializer_list<_Ux>&, _CTypes...>
&& _Call::template _Is_callable_from<decay_t<_Fn>>;

template <class...>
friend class move_only_function;

template <class _Vt>
static constexpr bool _Is_move_only_fuction_varying_cv_ref_noex() {
if constexpr (_Is_specialization_v<_Vt, move_only_function>) {
return is_same_v<typename _Call::_Signature_without_cv_ref_noex,
typename _Vt::_Signature_without_cv_ref_noex>;
} else {
return false;
}
}

public:
using typename _Call::result_type;

Expand All @@ -2079,7 +2093,7 @@ public:
using _Vt = decay_t<_Fn>;
static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. "
"(N4950 [func.wrap.move.ctor]/6)");
if constexpr (is_same_v<_Vt, function<_Signature...>>) {
if constexpr (is_same_v<_Vt, function<typename _Call::_Signature_without_cv_ref_noex>>) {
this->template _Construct_with_old_fn<_Vt>(_STD forward<_Fn>(_Callable));
} else {
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt>
Expand All @@ -2090,8 +2104,13 @@ public:
}
}

using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
if constexpr (_Is_move_only_fuction_varying_cv_ref_noex<_Vt>()) {
_Call::_Checked_move(this->_Data, _Callable._Data);
_Callable._Reset_to_null();
} else {
using _VtInvQuals = _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
}
}
}

Expand Down
40 changes: 37 additions & 3 deletions tests/std/tests/GH_005504_avoid_function_call_wrapping/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,18 @@ struct copy_counter {
int count = 0;
};

using fn_type = int(copy_counter);
using fn_type = int(copy_counter);
using fn_type_r = int(copy_counter) &;
using fn_type_c = int(copy_counter) const;

#ifdef __cpp_noexcept_function_type
using fn_type_nx = int(copy_counter) noexcept;
#endif // defined(__cpp_noexcept_function_type)

struct small_callable {
const int context = 42;

int operator()(const copy_counter& counter) {
int operator()(const copy_counter& counter) const noexcept {
assert(context == 42);
return counter.count;
}
Expand All @@ -83,7 +89,7 @@ struct small_callable {
struct alignas(128) large_callable {
const int context = 1729;

int operator()(const copy_counter& counter) {
int operator()(const copy_counter& counter) const noexcept {
assert((reinterpret_cast<uintptr_t>(this) & 0x7f) == 0);
assert(context == 1729);
return counter.count;
Expand Down Expand Up @@ -156,13 +162,41 @@ int main() {
alloc_checker{0}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type>, large_callable>(0);

// Abominables and noexcept specifier
alloc_checker{0}, test_wrapped_call<move_only_function<fn_type_r>, move_only_function<fn_type>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type_r>, move_only_function<fn_type>, large_callable>(0);
alloc_checker{0}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type_c>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type_c>, large_callable>(0);

static_assert(!is_constructible_v<move_only_function<fn_type>, move_only_function<fn_type_r>>);
static_assert(!is_constructible_v<move_only_function<fn_type_c>, move_only_function<fn_type>>);

#ifdef __cpp_noexcept_function_type
alloc_checker{0}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type_nx>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type>, move_only_function<fn_type_nx>, large_callable>(0);

static_assert(!is_constructible_v<move_only_function<fn_type_nx>, move_only_function<fn_type>>);
#endif // defined(__cpp_noexcept_function_type)

constexpr bool is_64_bit = sizeof(void*) > 4;

// Moves from function to move_only_function
alloc_checker{is_64_bit ? 0 : 1},
test_wrapped_call<move_only_function<fn_type>, function<fn_type>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type>, function<fn_type>, large_callable>(0);

// Moves from function to abominable move_only_function
alloc_checker{is_64_bit ? 0 : 1},
test_wrapped_call<move_only_function<fn_type_r>, function<fn_type>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type_r>, function<fn_type>, large_callable>(0);
alloc_checker{is_64_bit ? 0 : 1},
test_wrapped_call<move_only_function<fn_type_c>, function<fn_type>, small_callable>(0);
alloc_checker{1}, test_wrapped_call<move_only_function<fn_type_c>, function<fn_type>, large_callable>(0);

#ifdef __cpp_noexcept_function_type
static_assert(!is_constructible_v<move_only_function<fn_type_nx>, function<fn_type>>);
#endif // defined(__cpp_noexcept_function_type)

alloc_checker{is_64_bit ? 0 : 1},
test_wrapped_copy_call<move_only_function<fn_type>, function<fn_type>, small_callable>(0);
alloc_checker{2}, test_wrapped_copy_call<move_only_function<fn_type>, function<fn_type>, large_callable>(0);
Expand Down