diff --git a/docs/changelog.rst b/docs/changelog.rst index 5f38876d..e117b506 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -43,6 +43,12 @@ Version TBD (unreleased) of storing a dangling C++ iterator reference in the returned Python iterator object. (PR `#788 `__) +- Bindings for augmented assignment operators (as generated, for example, by + ``.def(nb::self += nb::self)``) now return the same object in Python in the + typical case where the C++ operator returns a reference to ``*this``. + Previously, after ``a += b``, ``a`` would be replaced with a copy. + (PR `#803 `__) + Version 2.2.0 (October 3, 2024) ------------------------------- diff --git a/include/nanobind/operators.h b/include/nanobind/operators.h index 55f8b068..e8ebb142 100644 --- a/include/nanobind/operators.h +++ b/include/nanobind/operators.h @@ -45,7 +45,7 @@ template struct op_ { using Lt = std::conditional_t, Type, L>; using Rt = std::conditional_t, Type, R>; using Op = op_impl; - cl.def(Op::name(), &Op::execute, is_operator(), extra...); + cl.def(Op::name(), &Op::execute, is_operator(), Op::default_policy, extra...); } template void execute_cast(Class &cl, const Extra&... extra) const { @@ -53,17 +53,19 @@ template struct op_ { using Lt = std::conditional_t, Type, L>; using Rt = std::conditional_t, Type, R>; using Op = op_impl; - cl.def(Op::name(), &Op::execute_cast, is_operator(), extra...); + cl.def(Op::name(), &Op::execute_cast, is_operator(), Op::default_policy, extra...); } }; #define NB_BINARY_OPERATOR(id, rid, op, expr) \ template struct op_impl { \ + static constexpr rv_policy default_policy = rv_policy::automatic; \ static char const* name() { return "__" #id "__"; } \ static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ static B execute_cast(const L &l, const R &r) { return B(expr); } \ }; \ template struct op_impl { \ + static constexpr rv_policy default_policy = rv_policy::automatic; \ static char const* name() { return "__" #rid "__"; } \ static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ static B execute_cast(const R &r, const L &l) { return B(expr); } \ @@ -80,6 +82,7 @@ template op_ op(const T &, const self_t & #define NB_INPLACE_OPERATOR(id, op, expr) \ template struct op_impl { \ + static constexpr rv_policy default_policy = rv_policy::move; \ static char const* name() { return "__" #id "__"; } \ static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ static B execute_cast(L &l, const R &r) { return B(expr); } \ @@ -90,6 +93,7 @@ template op_ op(const self_t &, const T & #define NB_UNARY_OPERATOR(id, op, expr) \ template struct op_impl { \ + static constexpr rv_policy default_policy = rv_policy::automatic; \ static char const* name() { return "__" #id "__"; } \ static auto execute(const L &l) -> decltype(expr) { return expr; } \ static B execute_cast(const L &l) { return B(expr); } \ diff --git a/tests/test_classes.py b/tests/test_classes.py index 42f755cd..cdf1939c 100644 --- a/tests/test_classes.py +++ b/tests/test_classes.py @@ -325,7 +325,9 @@ def test14_operators(): assert repr(a - b) == "3" assert "unsupported operand type" in str(excinfo.value) assert repr(a - 2) == "-1" + a_before = id(a) a += b + assert id(a) == a_before assert repr(a) == "3" assert repr(b) == "2"