Skip to content
Merged
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
14 changes: 10 additions & 4 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,22 @@ template <class _Fx>
struct _Ref_fn { // pass function object by value as a reference
template <class... _Args>
constexpr decltype(auto) operator()(_Args&&... _Vals) { // forward function call operator
return _Fn(_STD forward<_Args>(_Vals)...);
#if _HAS_IF_CONSTEXPR
if constexpr (is_member_pointer_v<_Fx>) {
return _STD invoke(_Fn, _STD forward<_Args>(_Vals)...);
} else
#endif // _HAS_IF_CONSTEXPR
{
return _Fn(_STD forward<_Args>(_Vals)...);
}
}

_Fx& _Fn;
};

template <class _Fn>
_INLINE_VAR constexpr bool
_Pass_functor_by_value_v = sizeof(_Fn) <= sizeof(void*)
&& conjunction_v<is_trivially_copy_constructible<_Fn>, is_trivially_destructible<_Fn>>;
_INLINE_VAR constexpr bool _Pass_functor_by_value_v = conjunction_v<bool_constant<sizeof(_Fn) <= sizeof(void*)>,
is_trivially_copy_constructible<_Fn>, is_trivially_destructible<_Fn>>;

template <class _Fn, enable_if_t<_Pass_functor_by_value_v<_Fn>, int> = 0> // TRANSITION, if constexpr
constexpr _Fn _Pass_fn(_Fn _Val) { // pass functor by value
Expand Down
54 changes: 54 additions & 0 deletions tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,3 +800,57 @@ namespace sortable_test {
}
}
} // namespace sortable_test

namespace gh_1089 {
// Defend against regression of GH-1089: "_Pass_fn/_Ref_fn interferes with the Ranges invoke protocol"
// The _Pass_fn protocol would previously assume that anything larger than a pointer was a function object that it
// could call with `()` and not a pointer-to-member that requires the `invoke` protocol.

void test() {
{
struct Base {
virtual int purr() = 0;
};

struct Derived1 : virtual Base {
int purr() override {
return 1729;
}
};

struct Derived2 : virtual Base {};

struct MostDerived : Derived1, Derived2 {
int purr() override {
return 2020;
}
};


STATIC_ASSERT(sizeof(&Derived1::purr) > sizeof(void*)); // NB: relies on non-portable platform properties

Derived1 a[2];
MostDerived b[3];
Derived1* pointers[] = {&b[0], &a[0], &b[1], &a[1], &b[2]};

(void) ranges::count(pointers, 2020, &Derived1::purr);
}
{
struct Cat;

using PMD_Cat = int Cat::*;
// Quantum effects: we must observe the size before defining Cat or it will become smaller.
STATIC_ASSERT(sizeof(PMD_Cat) > sizeof(void*));

struct Cat {
int x = 42;
};

STATIC_ASSERT(sizeof(&Cat::x) > sizeof(void*)); // NB: relies on non-portable platform properties

Cat cats[42];

(void) ranges::count(cats, 42, &Cat::x);
}
}
Comment thread
CaseyCarter marked this conversation as resolved.
} // namespace gh_1089