diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index dee2423de0..ac13616d2a 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -72,20 +72,38 @@ class cpp_function : public function { (detail::function_signature_t *) nullptr, extra...); } - /// Construct a cpp_function from a class method (non-const) + /// Construct a cpp_function from a class method (non-const, no ref-qualifier) template cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*) (Class *, Arg...)) nullptr, extra...); } - /// Construct a cpp_function from a class method (const) + /// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier) + /// A copy of the overload for non-const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const, no ref-qualifier) template cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*)(const Class *, Arg ...)) nullptr, extra...); } + /// Construct a cpp_function from a class method (const, lvalue ref-qualifier) + /// A copy of the overload for const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + /// Return the function name object name() const { return attr("__name__"); } diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index c7b82f13d0..1337620941 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -207,6 +207,14 @@ class RegisteredDerived : public UnregisteredBase { double sum() const { return rw_value + ro_value; } }; +// Test explicit lvalue ref-qualification +struct RefQualified { + int value = 0; + + void refQualified(int other) & { value += other; } + int constRefQualified(int other) const & { return value + other; } +}; + TEST_SUBMODULE(methods_and_attributes, m) { // test_methods_and_attributes py::class_ emna(m, "ExampleMandA"); @@ -457,4 +465,11 @@ TEST_SUBMODULE(methods_and_attributes, m) { m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); + + // test_methods_and_attributes + py::class_(m, "RefQualified") + .def(py::init<>()) + .def_readonly("value", &RefQualified::value) + .def("refQualified", &RefQualified::refQualified) + .def("constRefQualified", &RefQualified::constRefQualified); } diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index f1c862be85..5a10fbfa45 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -510,3 +510,14 @@ def test_custom_caster_destruction(): # Make sure we still only have the original object (from ..._no_destroy()) alive: assert cstats.alive() == 1 + + +def test_ref_qualified(): + """Tests that explicit lvalue ref-qualified methods can be called just like their + non ref-qualified counterparts.""" + + r = m.RefQualified() + assert r.value == 0 + r.refQualified(17) + assert r.value == 17 + assert r.constRefQualified(23) == 40