Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing unsafe dereferencing of std::unique_ptr

Taken from a slide at cppcon2015:

unique_ptr<A> f() {
   auto a = make_unique<A>();
   return a;
}

//Why does this even compile?
const A & dangling = *f(); 

//BOOM!!!
use(dangling);

My question is: with rvalue references for *this, can this be solved?

I see in the spec in cppreference:

typename std::add_lvalue_reference<T>::type operator*() const;

Question:

  1. Would it make sense to disallow operator* for rvalue unique_ptrs and only have dereference valid for lvalue unique_ptrs?
  2. There are still valid use cases to keep the rvalue unique_ptr dereferenceable?

Like this:

//Make sure it is an lvalue.
typename std::add_lvalue_reference<T>::type operator*() const &;

NOTE: I am not sure of the syntax or correctness, I have no experience with rvalue references for *this.

like image 280
Germán Diago Avatar asked Oct 20 '22 01:10

Germán Diago


1 Answers

My question is: with rvalue references for *this, can this be solved?

Technically yes. One solution would be to introduce an additional (deleted) overload for rvalues:

typename std::add_lvalue_reference<T>::type operator*() const&& = delete;
//                                                      ~~~~~~~~~~~~~~~^

and to modify the existing one by means of adding a ref-qualifier:

typename std::add_lvalue_reference<T>::type operator*() const&;
//                                                         ~~^~~

Since rvalues strongly prefer to be bound by an rvalue reference, any attempt to dereference an rvalue expression involving a unique_ptr would result in a compile error - "use of deleted function".

Would it make sense to disallow operator* for rvalue unique_ptrs and only have dereference valid for lvalue unique_ptrs?

Not always. And because of that I doubt the library should impose additional constraints on the unique_ptr specification, only to prevent a possible misuse.

There are still valid use cases to keep the rvalue unique_ptr dereferenceable?

The lifetime of a temporary ends at the end of a full expression that the temporary is part of. This means that the object obtained from dereferencing a unique_ptr is valid as long as the associated unique_ptr is alive, so the below use-cases are valid, and wouldn't be possible if operator* was disabled for rvalues:

(*f()).foo();
//          ^~~ unique_ptr is destroyed here


use(*f());
//       ^~~ unique_ptr is destroyed here
like image 152
Piotr Skotnicki Avatar answered Oct 21 '22 16:10

Piotr Skotnicki