diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h index edf61de7..10244b8d 100644 --- a/absl/functional/function_ref.h +++ b/absl/functional/function_ref.h @@ -85,13 +85,12 @@ class FunctionRef; // callback); template class FunctionRef { - private: + protected: // Used to disable constructors for objects that are not compatible with the // signature of this FunctionRef. - template > + template using EnableIfCompatible = - std::enable_if_t, std::true_type, - std::is_invocable_r>::value>; + std::enable_if_t::value>; public: // Constructs a FunctionRef from any invocable type. @@ -111,9 +110,8 @@ class FunctionRef { // // This overload is also used for references to functions, since references to // functions can decay to function pointers implicitly. - template < - typename F, typename = EnableIfCompatible, - absl::functional_internal::EnableIf::value> = 0> + template , + absl::functional_internal::EnableIf> = 0> // NOLINTNEXTLINE(google-explicit-constructor) FunctionRef(F* f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : invoker_(&absl::functional_internal::InvokeFunction) { @@ -126,22 +124,27 @@ class FunctionRef { // `F` at compile time. This allows calling arbitrary functions while avoiding // an indirection. // Needs C++20 as `nontype_t` needs C++20 for `auto` template parameters. - template + template > FunctionRef(nontype_t) noexcept // NOLINT(google-explicit-constructor) : invoker_(&absl::functional_internal::InvokeFunction) {} - template + template < + auto F, typename Obj, + typename = EnableIfCompatible&>, + absl::functional_internal::EnableIf> = + 0> // NOLINTNEXTLINE(google-explicit-constructor) - FunctionRef(nontype_t, Obj&& obj) noexcept + FunctionRef(nontype_t, Obj&& obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : invoker_(&absl::functional_internal::InvokeObject) { ptr_.obj = std::addressof(obj); } - template + template > // NOLINTNEXTLINE(google-explicit-constructor) - FunctionRef(nontype_t, Obj* obj) noexcept + FunctionRef(nontype_t, Obj* obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : invoker_(&absl::functional_internal::InvokePtr) { ptr_.obj = obj; @@ -161,42 +164,72 @@ class FunctionRef { // Allow const qualified function signatures. Since FunctionRef requires // constness anyway we can just make this a no-op. template -class FunctionRef : public FunctionRef { +class FunctionRef : private FunctionRef { using Base = FunctionRef; - template - using EnableIfCallable = - std::enable_if_t> && - std::is_invocable_r_v && - std::is_constructible_v, - T>; + template + using EnableIfCompatible = + typename Base::template EnableIfCompatible; public: - template > + template < + typename F, + typename = EnableIfCompatible>, const F&>>> // NOLINTNEXTLINE(google-explicit-constructor) FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(f) {} - template >> + template , + absl::functional_internal::EnableIf> = 0> // NOLINTNEXTLINE(google-explicit-constructor) FunctionRef(F* f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(f) {} #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L - template > + template > // NOLINTNEXTLINE(google-explicit-constructor) FunctionRef(nontype_t arg) noexcept : Base(arg) {} - template > + template &>, + absl::functional_internal::EnableIf< + !std::is_rvalue_reference_v> = 0> // NOLINTNEXTLINE(google-explicit-constructor) - FunctionRef(nontype_t arg, Obj&& obj) noexcept + FunctionRef(nontype_t arg, + Obj&& obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(arg, std::forward(obj)) {} - template > + template > // NOLINTNEXTLINE(google-explicit-constructor) - FunctionRef(nontype_t arg, Obj* obj) noexcept : Base(arg, obj) {} + FunctionRef(nontype_t arg, + const Obj* obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept + : Base(arg, obj) {} #endif + + using Base::operator(); }; +template +FunctionRef(F*) -> FunctionRef; + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L +template +FunctionRef(nontype_t) + -> FunctionRef>; + +template +FunctionRef(nontype_t, U&&) + -> FunctionRef, M>>; + +template +FunctionRef(nontype_t, U&&) -> FunctionRef, std::invoke_result_t()>>; + +template +FunctionRef(nontype_t, U&&) -> FunctionRef; +#endif + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc index c8ff0804..26db103b 100644 --- a/absl/functional/function_ref_test.cc +++ b/absl/functional/function_ref_test.cc @@ -30,6 +30,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace { +struct Class { + int Func() { return 42; } + int CFunc() const { return 43; } +}; + int Function() { return 1337; } template @@ -312,6 +317,7 @@ TEST(FunctionRefTest, CorrectConstQualifiers) { EXPECT_EQ(42, FunctionRef(s)()); EXPECT_EQ(1337, FunctionRef(s)()); EXPECT_EQ(1337, FunctionRef(std::as_const(s))()); + EXPECT_EQ(1337, FunctionRef(std::as_const(s))()); } TEST(FunctionRefTest, Lambdas) { @@ -342,11 +348,46 @@ TEST(FunctionRefTest, Lambdas) { } #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L -TEST(FunctionRefTest, NonTypeParameter) { - EXPECT_EQ(1337, FunctionRef(nontype<&Function>)()); - EXPECT_EQ(42, FunctionRef(nontype<&Copy>, 42)()); - EXPECT_EQ(42, FunctionRef(nontype<&Dereference>, - &std::integral_constant::value)()); +static_assert(std::is_same_v, + std::declval())), + FunctionRef>); +static_assert(std::is_same_v, + std::declval())), + FunctionRef>); + +static_assert(std::is_same_v, + std::declval())), + FunctionRef>); +static_assert(std::is_same_v, + std::declval())), + FunctionRef>); + +TEST(FunctionRefTest, NonTypeParameterWithTemporaries) { + static_assert(!std::is_constructible_v, + nontype_t<&Class::Func>, Class&&>); + static_assert( + !std::is_constructible_v, nontype_t<&Class::Func>, + const Class&&>); + static_assert(!std::is_constructible_v, + nontype_t<&Class::CFunc>, Class&&>); + static_assert( + !std::is_constructible_v, + nontype_t<&Class::CFunc>, const Class&&>); +} + +TEST(FunctionRefTest, NonTypeParameterWithDeductionGuides) { + EXPECT_EQ(1337, FunctionRef(nontype<&Function>)()); + EXPECT_EQ(42, FunctionRef(nontype<&Copy>, + std::integral_constant::value)()); + EXPECT_EQ(42, FunctionRef(nontype<&Dereference>, + &std::integral_constant::value)()); + + Class c; + EXPECT_EQ(42, FunctionRef(nontype<&Class::Func>, c)()); + EXPECT_EQ(43, FunctionRef(nontype<&Class::CFunc>, c)()); + + EXPECT_EQ(42, FunctionRef(nontype<&Class::Func>, &c)()); + EXPECT_EQ(43, FunctionRef(nontype<&Class::CFunc>, &c)()); } #endif diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h index 0796364a..543b30f6 100644 --- a/absl/functional/internal/function_ref.h +++ b/absl/functional/internal/function_ref.h @@ -81,22 +81,37 @@ R InvokeObject(VoidPtr ptr, typename ForwardT::type... args) { template R InvokeObject(VoidPtr ptr, typename ForwardT::type... args) { using T = std::remove_reference_t; - return static_cast( - F(std::forward(*const_cast(static_cast(ptr.obj))), + Obj&& obj = + std::forward(*const_cast(static_cast(ptr.obj))); + // Avoid std::invoke() since the callee is a known function at compile time + if constexpr (std::is_member_function_pointer_v) { + return static_cast((std::forward(obj).*F)( std::forward::type>(args)...)); + } else { + return static_cast( + F(std::forward(obj), + std::forward::type>(args)...)); + } } template R InvokePtr(VoidPtr ptr, typename ForwardT::type... args) { - return static_cast( - F(const_cast(static_cast(ptr.obj)), - std::forward::type>(args)...)); + T* obj = const_cast(static_cast(ptr.obj)); + // Avoid std::invoke() since the callee is a known function at compile time + if constexpr (std::is_member_function_pointer_v) { + return static_cast( + (obj->*F)(std::forward::type>(args)...)); + } else { + return static_cast( + F(obj, std::forward::type>(args)...)); + } } template R InvokeFunction(VoidPtr ptr, typename ForwardT::type... args) { auto f = reinterpret_cast(ptr.fun); - return static_cast(std::invoke(f, std::forward(args)...)); + return static_cast( + std::invoke(f, std::forward::type>(args)...)); } template