It has always helped me to think of it as though the object being returned (A
) extends the lifetime of its owning object (B
) to be at least as long as the returned object (A
).
The return_internal_reference
documentation describes the owner_arg
:
The index of the parameter which contains the object to which the reference or pointer is being returned. If used to wrap a member function, parameter
1
is the target object (*this
).
In the original code, the owner_arg
is explicitly set to 1
indicating that the this
object (B
) in the member function (&B::a
) invocation is the object that contains the returned object (iA
).
The lifetime behavior effects is documented in with_custodian_and_ward_postcall
, which states:
The ward object will not be destroyed until after the custodian [...]
The return_internal_reference
documentation's class synopsis simplifies the actual inheritance chain:
template <std::size_t owner_arg = 1, class BasePolicy_ = default_call_policies>
struct return_internal_reference
: with_custodian_and_ward_postcall<0, owner_arg, BasePolicy_>
{
// ...
};
The return_internal_reference
struct:
- explicitly provides
0
as thecustodian
, setting the return object (iA
) ofpostcall()
as the custodian in the relationship. - passes the
owner_arg
(return_internal_reference
defaults to1
) as theward
, setting the*this
object (B
) as the ward in the relationship.
Hence, the B
ward object will not be destroyed until after the iA
custodian object.
Here is a complete simple example demonstrating this behavior:
#include <iostream>
#include <boost/python.hpp>
class Foo
{
public:
Foo() { std::cout << "Foo()" << std::endl; }
~Foo() { std::cout << "~Foo()" << std::endl; }
};
class Bar
{
public:
Bar() { std::cout << "Bar()" << std::endl; }
~Bar() { std::cout << "~Bar()" << std::endl; }
Foo& foo() { return foo_; }
private:
Foo foo_;
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Foo>("Foo");
python::class_<Bar>("Bar")
.def("foo", &Bar::foo, python::return_internal_reference<>())
;
}
Interactive Python:
>>> import example
>>> bar = example.Bar()
Foo()
Bar()
>>> foo = bar.foo()
>>> del bar
>>> del foo
~Bar()
~Foo()
Note that the lifetime of the bar
ward object was extended to be at least as long as the foo
custodian object.