diff --git a/stl/inc/xutility b/stl/inc/xutility index f80ed07fa86..565eaee50d9 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -190,16 +190,22 @@ template struct _Ref_fn { // pass function object by value as a reference template 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 -_INLINE_VAR constexpr bool - _Pass_functor_by_value_v = sizeof(_Fn) <= sizeof(void*) - && conjunction_v, is_trivially_destructible<_Fn>>; +_INLINE_VAR constexpr bool _Pass_functor_by_value_v = conjunction_v, + is_trivially_copy_constructible<_Fn>, is_trivially_destructible<_Fn>>; template , int> = 0> // TRANSITION, if constexpr constexpr _Fn _Pass_fn(_Fn _Val) { // pass functor by value diff --git a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp index ff3beb645d5..7628e82238b 100644 --- a/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_algorithm_machinery/test.cpp @@ -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); + } + } +} // namespace gh_1089