Skip to content

Commit 63df87f

Browse files
Add lvalue ref-qualified cpp_function constructors (#2213)
* added overload for l-value ref-qualified methods * Added test. Before, the code would have failed to build.
1 parent b524008 commit 63df87f

File tree

3 files changed

+46
-2
lines changed

3 files changed

+46
-2
lines changed

include/pybind11/pybind11.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,38 @@ class cpp_function : public function {
7272
(detail::function_signature_t<Func> *) nullptr, extra...);
7373
}
7474

75-
/// Construct a cpp_function from a class method (non-const)
75+
/// Construct a cpp_function from a class method (non-const, no ref-qualifier)
7676
template <typename Return, typename Class, typename... Arg, typename... Extra>
7777
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
7878
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
7979
(Return (*) (Class *, Arg...)) nullptr, extra...);
8080
}
8181

82-
/// Construct a cpp_function from a class method (const)
82+
/// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier)
83+
/// A copy of the overload for non-const functions without explicit ref-qualifier
84+
/// but with an added `&`.
85+
template <typename Return, typename Class, typename... Arg, typename... Extra>
86+
cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) {
87+
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
88+
(Return (*) (Class *, Arg...)) nullptr, extra...);
89+
}
90+
91+
/// Construct a cpp_function from a class method (const, no ref-qualifier)
8392
template <typename Return, typename Class, typename... Arg, typename... Extra>
8493
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
8594
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
8695
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
8796
}
8897

98+
/// Construct a cpp_function from a class method (const, lvalue ref-qualifier)
99+
/// A copy of the overload for const functions without explicit ref-qualifier
100+
/// but with an added `&`.
101+
template <typename Return, typename Class, typename... Arg, typename... Extra>
102+
cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) {
103+
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
104+
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
105+
}
106+
89107
/// Return the function name
90108
object name() const { return attr("__name__"); }
91109

tests/test_methods_and_attributes.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ class RegisteredDerived : public UnregisteredBase {
207207
double sum() const { return rw_value + ro_value; }
208208
};
209209

210+
// Test explicit lvalue ref-qualification
211+
struct RefQualified {
212+
int value = 0;
213+
214+
void refQualified(int other) & { value += other; }
215+
int constRefQualified(int other) const & { return value + other; }
216+
};
217+
210218
TEST_SUBMODULE(methods_and_attributes, m) {
211219
// test_methods_and_attributes
212220
py::class_<ExampleMandA> emna(m, "ExampleMandA");
@@ -457,4 +465,11 @@ TEST_SUBMODULE(methods_and_attributes, m) {
457465
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
458466
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
459467
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);
468+
469+
// test_methods_and_attributes
470+
py::class_<RefQualified>(m, "RefQualified")
471+
.def(py::init<>())
472+
.def_readonly("value", &RefQualified::value)
473+
.def("refQualified", &RefQualified::refQualified)
474+
.def("constRefQualified", &RefQualified::constRefQualified);
460475
}

tests/test_methods_and_attributes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,14 @@ def test_custom_caster_destruction():
510510

511511
# Make sure we still only have the original object (from ..._no_destroy()) alive:
512512
assert cstats.alive() == 1
513+
514+
515+
def test_ref_qualified():
516+
"""Tests that explicit lvalue ref-qualified methods can be called just like their
517+
non ref-qualified counterparts."""
518+
519+
r = m.RefQualified()
520+
assert r.value == 0
521+
r.refQualified(17)
522+
assert r.value == 17
523+
assert r.constRefQualified(23) == 40

0 commit comments

Comments
 (0)