27

Consider the following program:

#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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 4
    Because then things like `int x; return &x;` would compile. – Kerrek SB Jan 19 '16 at 16:42
  • @KerrekSB: I can also do `int *p = nullptr; *p = 123;` and that will compile. Anyway, not to be too argumentative - I'll ask - what should I do, then? – einpoklum Jan 19 '16 at 16:46
  • 1
    You make one from your pointer, e.g. `std::unique_ptr(p)`. – Vincent Savard Jan 19 '16 at 16:48
  • @VincentSavard: But isn't that exactly what I'm doing by returning p as an `std::unique_ptr`? – einpoklum Jan 19 '16 at 16:50
  • 5
    No, you're returning `p`, which is of type `int*`, not `std::unique_ptr`. Maybe you do not understand what `auto` does. – Vincent Savard Jan 19 '16 at 16:51
  • `return std::make_unique();` [`std::make_unique`](http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) – Joe Jan 19 '16 at 16:51
  • @VincentSavard: The return type is `std::unique_ptr`, I can only return that. I mean, the int* is supposed to be cast. Just like if I returned 3.5 in a function returning int. – einpoklum Jan 19 '16 at 16:54
  • @Joe: Assume you can't avoid p. Somehow an `int* p` gets created and set (in a magical way which I've left out of the MWE). – einpoklum Jan 19 '16 at 16:55
  • 1
    And that's why the compiler doesn't compile this code. Is your question "Why isn't `p` implicitly cast to `std::unique_ptr`?" – Vincent Savard Jan 19 '16 at 16:56
  • @VincentSavard: If that were the case, I should not be able to compile `int foo() { return 3.5; }` and need to do `int foo() { return (int)3.5;}` - but I don't need that. – einpoklum Jan 19 '16 at 17:06
  • 1
    Because implicit conversion from `double` to `int` is defined. You're mixing different concepts. – Vincent Savard Jan 19 '16 at 17:07
  • @einpoklum: Still, that's a different level of wrong. Your `p` is not dereferenceable, which is your fault. My `&x` is a perfectly valid int pointer. – Kerrek SB Jan 19 '16 at 17:23
  • @cat: [cannot reproduce](https://ideone.com/F8XqFv) – Kerrek SB Jan 20 '16 at 09:30
  • @cat: also [cannot reproduce with GCC 5.2](http://melpon.org/wandbox/permlink/oEuJnSB1ciXX6NtA). – Kerrek SB Jan 20 '16 at 13:53

5 Answers5

28

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.

Ilya Popov
  • 3,765
  • 1
  • 17
  • 30
  • 1
    Thank you for bringing Mr. Sutter's proposal and the EWG discussion to my attention, that is most illuminating (and I have to also say - standing in contrast to some people's dismissive attitude). – einpoklum Jan 19 '16 at 20:53
  • 1
    "it is considered a good practice to declare all single-argument constructors as explicit to prevent accidental conversions" --- who considers this a good practice? The standard library itself doesn't follow this rule. For example, std::shared_ptr is implicitly constructible from std::unique_ptr. – Brian Bi Jan 16 '19 at 19:47
9

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);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
Stas
  • 11,571
  • 9
  • 40
  • 58
  • I can't say I understand how it would be error prone in a function returning a `unique_ptr`. Can you elaborate a bit more? – einpoklum Jan 19 '16 at 16:58
  • 6
    E.g. `foo(unique_ptr p) {}` in such case would be able to steal a passed naked pointer, and destroy the object. – Stas Jan 19 '16 at 17:01
  • Your example does not involve a function returning a unique_ptr, so - I don't follow... – einpoklum Jan 19 '16 at 17:04
  • Explicitness of an object construction is defined by a constructor definition. So, you can't do that implicitly when return the object, but explicitly in other cases. – Stas Jan 19 '16 at 17:06
  • So, you're saying this is related to the `explicit` keyword? – einpoklum Jan 19 '16 at 17:07
  • 2
    Yes. `unique_ptr` constructor from naked pointer is defined as explicit. – Stas Jan 19 '16 at 17:09
  • This answers OP's question well - raw pointers cannot be implicitly converted to smart pointers. – erip Jan 19 '16 at 17:12
5

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
}
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
  • 1
    That's not the reason IMO. `get_it` returns a unique pointer, it is obvious returning a pointer from means transferring ownership. – einpoklum Jan 19 '16 at 17:14
  • Good point, but I don't think C++ is smart enough to distinguish between this case and case in the question. So, both are allowed, or both are forbidden. – GingerPlusPlus Jan 19 '16 at 17:15
  • @einpoklum : This _is_ indeed the reason. – ildjarn Jan 19 '16 at 19:59
4

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);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 2
    Forgive me if this is a silly question, but why not just `make_unique`? – erip Jan 19 '16 at 17:18
  • @erip: Because this is an MWE. Actually we have `auto p = do_stuff_which_returns_a_raw_pointer()` – einpoklum Jan 19 '16 at 17:19
  • Right, but you could simply do `auto get_it() { auto p = new int; return std::make_unique(p); }`, could you not? I would think that forwarding would be preferred to explicit ctor call. – erip Jan 19 '16 at 17:21
  • @erip: In this example, yes. But, you know, I could just `int main() { return 0;}` since I don't do anything with `p`... – einpoklum Jan 19 '16 at 17:22
  • I think you're missing my point. I've edited my comment to be more reflective of my intent. – erip Jan 19 '16 at 17:23
  • @erip That's exactly what the bottom of my answer suggests. – einpoklum Jan 19 '16 at 17:25
  • 1
    Errr... No. There's a difference between constructing and forwarding an object. – erip Jan 19 '16 at 17:27
  • 2
    Also, why are you referring to yourself as OP? :) Typically self-answers should add something that other answers _haven't_. I think your answer is covered by @Stas 's answer. – erip Jan 19 '16 at 17:31
  • @erip: I think it would be more confusing if I wrote "oh, I can do this instead of that". Anyway, no, my answer is not covered by what Stas wrote; and I don't agree with part of his answer. – einpoklum Jan 19 '16 at 20:50
  • Maybe not, but definitely covered the comments which is part of @Stas 's answer. – erip Jan 19 '16 at 20:51
  • @erip: (shrugs) you're kind of splitting hairs here. I wrote up another answer because I want to express the way I understood it. This won't be the accepted answer anyway. – einpoklum Jan 19 '16 at 20:56
2

Because it is dangerous.

Use std::make_unique() from C++14:

std::unique_ptr<int> get_it()
{
    auto p = std::make_unique<int>();
    return p;
}
Victor Dyachenko
  • 1,363
  • 8
  • 18