diff --git a/include/eigenpy/std_unique_ptr.hpp b/include/eigenpy/std_unique_ptr.hpp
index 45ac265dcef49068f762990c25c927165632e7cd..15e1dd6c287237281a901375d4c295f32c0892cb 100644
--- a/include/eigenpy/std_unique_ptr.hpp
+++ b/include/eigenpy/std_unique_ptr.hpp
@@ -16,6 +16,7 @@ namespace eigenpy {
 
 namespace details {
 
+/// Transfer std::unique_ptr ownership to an owning holder
 template <typename T>
 typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
                         PyObject*>::type
@@ -27,6 +28,7 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) {
   }
 }
 
+/// Convert and copy the primitive value to python
 template <typename T>
 typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
                         PyObject*>::type
@@ -38,7 +40,30 @@ unique_ptr_to_python(std::unique_ptr<T>&& x) {
   }
 }
 
-}  // namespace details
+/// std::unique_ptr keep the ownership but a reference to the std::unique_ptr
+/// value is created
+template <typename T>
+typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
+                        PyObject*>::type
+internal_unique_ptr_to_python(std::unique_ptr<T>& x) {
+  if (!x) {
+    return bp::detail::none();
+  } else {
+    return bp::detail::make_reference_holder::execute(x.get());
+  }
+}
+
+/// Convert and copy the primitive value to python
+template <typename T>
+typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
+                        PyObject*>::type
+internal_unique_ptr_to_python(std::unique_ptr<T>& x) {
+  if (!x) {
+    return bp::detail::none();
+  } else {
+    return bp::to_python_value<const T&>()(*x);
+  }
+}
 
 /// result_converter of StdUniquePtrCallPolicies
 struct StdUniquePtrResultConverter {
@@ -60,12 +85,48 @@ struct StdUniquePtrResultConverter {
   };
 };
 
+/// result_converter of ReturnInternalStdUniquePtr
+struct InternalStdUniquePtrConverter {
+  template <typename T>
+  struct apply {
+    struct type {
+      typedef typename remove_cvref<T>::type::element_type element_type;
+
+      PyObject* operator()(T x) const {
+        return details::internal_unique_ptr_to_python(x);
+      }
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+      PyTypeObject const* get_pytype() const {
+        return bp::to_python_value<const element_type&>().get_pytype();
+      }
+#endif
+    };
+  };
+};
+
+}  // namespace details
+
 /// CallPolicies to get std::unique_ptr value from a function
 /// that return an std::unique_ptr.
 /// If the object inside the std::unique_ptr is a class or an union
 /// it will be moved. In other case, it will be copied.
 struct StdUniquePtrCallPolicies : bp::default_call_policies {
-  typedef StdUniquePtrResultConverter result_converter;
+  typedef details::StdUniquePtrResultConverter result_converter;
+};
+
+/// Variant of \see bp::return_internal_reference that extract std::unique_ptr
+/// content reference before converting it into a PyObject
+struct ReturnInternalStdUniquePtr : bp::return_internal_reference<> {
+  typedef details::InternalStdUniquePtrConverter result_converter;
+
+  template <class ArgumentPackage>
+  static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) {
+    // Don't run return_internal_reference postcall on primitive type
+    if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) {
+      return result;
+    }
+    return bp::return_internal_reference<>::postcall(args_, result);
+  }
 };
 
 }  // namespace eigenpy
diff --git a/include/eigenpy/utils/traits.hpp b/include/eigenpy/utils/traits.hpp
index 9ce6b4fae5c8a8cd63defe1f622e299f56342e42..1f78a8d56f61acad3c5089d8b32484bb5e34ba9a 100644
--- a/include/eigenpy/utils/traits.hpp
+++ b/include/eigenpy/utils/traits.hpp
@@ -17,11 +17,14 @@ struct is_class_or_union
     : std::integral_constant<bool, std::is_class<T>::value ||
                                        std::is_union<T>::value> {};
 
+template <typename T>
+struct remove_cvref : std::remove_cv<typename std::remove_reference<T>::type> {
+};
+
 /// Trait to remove cvref and call is_class_or_union
 template <typename T>
 struct is_class_or_union_remove_cvref
-    : is_class_or_union<typename std::remove_cv<
-          typename std::remove_reference<T>::type>::type> {};
+    : is_class_or_union<typename remove_cvref<T>::type> {};
 
 }  // namespace details
 
diff --git a/unittest/python/test_std_unique_ptr.py b/unittest/python/test_std_unique_ptr.py
index cb8f7d462c033c3d928b38f42564312a85a13648..6feb408f8c4b93c90d338cc398b88c92c04fdcae 100644
--- a/unittest/python/test_std_unique_ptr.py
+++ b/unittest/python/test_std_unique_ptr.py
@@ -1,4 +1,10 @@
-from std_unique_ptr import make_unique_int, make_unique_v1, make_unique_null, V1
+from std_unique_ptr import (
+    make_unique_int,
+    make_unique_v1,
+    make_unique_null,
+    V1,
+    UniquePtrHolder,
+)
 
 v = make_unique_int()
 assert isinstance(v, int)
@@ -10,3 +16,23 @@ assert v.v == 10
 
 v = make_unique_null()
 assert v is None
+
+unique_ptr_holder = UniquePtrHolder()
+
+v = unique_ptr_holder.int_ptr
+assert isinstance(v, int)
+assert v == 20
+# v is a copy, int_ptr will not be updated
+v = 10
+assert unique_ptr_holder.int_ptr == 20
+
+v = unique_ptr_holder.v1_ptr
+assert isinstance(v, V1)
+assert v.v == 200
+# v is a ref, v1_ptr will be updated
+v.v = 10
+assert unique_ptr_holder.v1_ptr.v == 10
+
+
+v = unique_ptr_holder.null_ptr
+assert v is None
diff --git a/unittest/std_unique_ptr.cpp b/unittest/std_unique_ptr.cpp
index 2fdad8c33085c1baaed09ab14a88d3ca1d89e313..9c163ac88f27401bac2f73819afd347840e009b4 100644
--- a/unittest/std_unique_ptr.cpp
+++ b/unittest/std_unique_ptr.cpp
@@ -22,6 +22,9 @@ std::unique_ptr<V1> make_unique_v1() { return std::make_unique<V1>(10); }
 std::unique_ptr<V1> make_unique_null() { return nullptr; }
 
 struct UniquePtrHolder {
+  UniquePtrHolder()
+      : int_ptr(std::make_unique<int>(20)), v1_ptr(std::make_unique<V1>(200)) {}
+
   std::unique_ptr<int> int_ptr;
   std::unique_ptr<V1> v1_ptr;
   std::unique_ptr<V1> null_ptr;
@@ -38,8 +41,16 @@ BOOST_PYTHON_MODULE(std_unique_ptr) {
           eigenpy::StdUniquePtrCallPolicies());
   bp::def("make_unique_null", make_unique_null,
           eigenpy::StdUniquePtrCallPolicies());
-  // TODO allow access with a CallPolicie like return_internal_reference
-  // boost::python::class_<UniquePtrHolder>("UniquePtrHolder", bp::init<>())
-  //     .add_property("int_ptr", bp::make_getter(&UniquePtrHolder::int_ptr),
-  //                   bp::make_setter(&UniquePtrHolder::int_ptr));
+
+  boost::python::class_<UniquePtrHolder, boost::noncopyable>("UniquePtrHolder",
+                                                             bp::init<>())
+      .add_property("int_ptr",
+                    bp::make_getter(&UniquePtrHolder::int_ptr,
+                                    eigenpy::ReturnInternalStdUniquePtr()))
+      .add_property("v1_ptr",
+                    bp::make_getter(&UniquePtrHolder::v1_ptr,
+                                    eigenpy::ReturnInternalStdUniquePtr()))
+      .add_property("null_ptr",
+                    bp::make_getter(&UniquePtrHolder::null_ptr,
+                                    eigenpy::ReturnInternalStdUniquePtr()));
 }