Why can't a pointer be automatically converted into a unique_ptr when returning it?
Solution 1
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 declaredexplicit
, thus considered by the compiler only in explicit contexts. This is done so to prevent accidental dangerous conversions, where aunique_ptr
can hijack a pointer and delete it without programmer's knowledge. In general, not only forunique_ptr
, it is considered a good practice to declare all single-argument constructors asexplicit
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.
Solution 2
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);
}
Solution 3
Because std::unique_ptr
takes ownership of the pointer, and you definitely don't want to get your raw pointer delete
d 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
}
Solution 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);
}
Related videos on Youtube
einpoklum
Made my way from the Olympus of Complexity Theory, Probabilistic Combinatorics and Property Testing to the down-to-earth domain of Heterogeneous and GPU Computing, and now I'm hoping to bring the gospel of GPU and massive-regularized parallelism to DBMS architectures. I've post-doc'ed at the DB architecture group in CWI Amsterdam to do (some of) that. I subscribe to most of Michael Richter's critique of StackOverflow; you might want to take the time to read it. If you listen closely you can hear me muttering "Why am I not socratic again already?"
Updated on June 06, 2022Comments
-
einpoklum almost 2 years
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 aunique_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.
-
Kerrek SB over 8 yearsBecause then things like
int x; return &x;
would compile. -
einpoklum over 8 years@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? -
Vincent Savard over 8 yearsYou make one from your pointer, e.g.
std::unique_ptr<int>(p)
. -
einpoklum over 8 years@VincentSavard: But isn't that exactly what I'm doing by returning p as an
std::unique_ptr<int>
? -
Vincent Savard over 8 yearsNo, you're returning
p
, which is of typeint*
, notstd::unique_ptr<int>
. Maybe you do not understand whatauto
does. -
Joe over 8 years
return std::make_unique<int>();
std::make_unique
-
einpoklum over 8 years@VincentSavard: The return type is
std::unique_ptr<int>
, 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 over 8 years@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). -
Vincent Savard over 8 yearsAnd that's why the compiler doesn't compile this code. Is your question "Why isn't
p
implicitly cast tostd::unique_ptr
?" -
einpoklum over 8 years@VincentSavard: If that were the case, I should not be able to compile
int foo() { return 3.5; }
and need to doint foo() { return (int)3.5;}
- but I don't need that. -
Vincent Savard over 8 yearsBecause implicit conversion from
double
toint
is defined. You're mixing different concepts. -
Kerrek SB over 8 years@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 over 8 years@cat: cannot reproduce
-
Kerrek SB over 8 years@cat: also cannot reproduce with GCC 5.2.
-
-
einpoklum over 8 yearsI can't say I understand how it would be error prone in a function returning a
unique_ptr
. Can you elaborate a bit more? -
Stas over 8 yearsE.g.
foo(unique_ptr<int> p) {}
in such case would be able to steal a passed naked pointer, and destroy the object. -
einpoklum over 8 yearsYour example does not involve a function returning a unique_ptr, so - I don't follow...
-
Stas over 8 yearsExplicitness 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.
-
einpoklum over 8 yearsSo, you're saying this is related to the
explicit
keyword? -
Stas over 8 yearsYes.
unique_ptr
constructor from naked pointer is defined as explicit. -
erip over 8 yearsThis answers OP's question well - raw pointers cannot be implicitly converted to smart pointers.
-
einpoklum over 8 yearsThat's not the reason IMO.
get_it
returns a unique pointer, it is obvious returning a pointer from means transferring ownership. -
GingerPlusPlus over 8 yearsGood 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.
-
erip over 8 yearsForgive me if this is a silly question, but why not just
make_unique
? -
einpoklum over 8 years@erip: Because this is an MWE. Actually we have
auto p = do_stuff_which_returns_a_raw_pointer()
-
erip over 8 yearsRight, but you could simply do
auto get_it() { auto p = new int; return std::make_unique<int>(p); }
, could you not? I would think that forwarding would be preferred to explicit ctor call. -
einpoklum over 8 years@erip: In this example, yes. But, you know, I could just
int main() { return 0;}
since I don't do anything withp
... -
erip over 8 yearsI think you're missing my point. I've edited my comment to be more reflective of my intent.
-
einpoklum over 8 years@erip That's exactly what the bottom of my answer suggests.
-
erip over 8 yearsErrr... No. There's a difference between constructing and forwarding an object.
-
erip over 8 yearsAlso, 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.
-
ildjarn over 8 years@einpoklum : This is indeed the reason.
-
einpoklum over 8 years@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.
-
erip over 8 yearsMaybe not, but definitely covered the comments which is part of @Stas 's answer.
-
einpoklum over 8 yearsThank 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 over 8 years@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.
-
Brian Bi over 5 years"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<T> is implicitly constructible from std::unique_ptr<T>.