Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a pointer be automatically converted into a unique_ptr when returning it?

Let me pose my question through an example.

#include <memory>

std::unique_ptr<int> get_it() {
        auto p = new int;
        return p;
}

int main() {
        auto up ( get_it() );
        return 0;
}

This fails to compile with the following error:

a.cpp:5:9: error: could not convert ‘p’ from ‘int*’ to ‘std::unique_ptr<int>’
  return p;
         ^

Why isn't there an automatic conversion from a raw pointer to a unique one here? And what should I be doing instead?

Motivation: I understand it's supposed to be good practice to use smart pointers for ownership to be clear; I'm getting a pointer (which I own) from somewhere, as an int* in this case, and I (think I) want it in a unique_ptr.


If you're considering commenting or adding your own answer, please address Herbert Sutter's arguments for this to be possible in proposal N4029.

like image 277
einpoklum Avatar asked Jan 19 '16 16:01

einpoklum


4 Answers

The answer is two-fold. All the other answers, including a OP's self-answer, addressed only one half of it.

The pointer cannot be automatically converted because:

  • unique_ptr's constructor from a pointer is declared explicit, thus considered by the compiler only in explicit contexts. This is done so to prevent accidental dangerous conversions, where a unique_ptr can hijack a pointer and delete it without programmer's knowledge. In general, not only for unique_ptr, it is considered a good practice to declare all single-argument constructors as explicit to prevent accidental conversions.
  • return statement is considered by the standard an implicit context, and thus the explicit constructor is not applicable. There was an ongoing discussion if this decision is right, reflected in EWG issue 114, including links to several proposals: two versions of proposals to make return explicit by Herb Sutter (N4029, N4074), and two "responses", arguing not to do so: N4094 by Howard Hinnant and Ville Voutilainen and N4131 by Filip Roséen. After several discussions and polls the issue was closed as NAD - not a defect.

Currently, there are several workarounds:

return std::unique_ptr<int>{p};

or

return std::unique_ptr<int>(p);

In c++14, you can use auto-deduction of function return type as well:

auto get_it() {
    auto p = new int;
    return std::unique_ptr<int>(p);
}

Update: added a link to committee issue for the second point.

like image 135
Ilya Popov Avatar answered Nov 19 '22 11:11

Ilya Popov


Because implicit construction of unique_ptr from naked pointer would be very error-prone.

Just construct it explicitly:

std::unique_ptr<int> get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}
like image 21
Stas Avatar answered Nov 19 '22 11:11

Stas


Because std::unique_ptr takes ownership of the pointer, and you definitely don't want to get your raw pointer deleted accidentally.

If that would be possible, then:

void give_me_pointer(std::unique_ptr<int>) { /* whatever */ }

int main() {
    int *my_int = new int;
    give_me_pointer(my_int);
    // my_int is dangling pointer
}
like image 4
GingerPlusPlus Avatar answered Nov 19 '22 10:11

GingerPlusPlus


Because the conversion, or casting, requires an appropriate cast operator (on the source type) or constructor (on the target type). In this case it must be the following constructor of unique_ptr:

explicit unique_ptr( pointer p );

Which has the explicit keyword. your get_it() attempts implicit conversion, which explicit prevents. Instead, you have to construct the unique_ptr explicitly, as suggested by @Stas and @VincentSavard :

std::unique_ptr<int> get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}

or even, if we want to only say unique_ptr once...

auto get_it() {
        auto p = new int;
        return std::unique_ptr<int>(p);
}
like image 3
einpoklum Avatar answered Nov 19 '22 10:11

einpoklum