30

The following functions do not compile:

std::unique_ptr<int> foo()
{
    int* answer = new int(42);
    return answer;
}

std::unique_ptr<int> bar()
{
    return new int(42);
}

I find this a bit inconvenient. What was the rationale for making std::unique_ptr<T>(T*) explicit?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 4
    For the same reasons [`shared_ptr` is explicit](http://stackoverflow.com/questions/304093/why-shared-ptr-has-an-explicit-constructor). – jxh Jul 06 '12 at 18:48
  • Can you really use a `unique_ptr` in the return type like that? It doesn't make sense to me, the whole point is that you can't copy it. – cha0site Jul 06 '12 at 18:52
  • 7
    @cha0site: it's a temporary, so it's moved, not copied. – Mooing Duck Jul 06 '12 at 18:52
  • 1
    @MooingDuck : Even if it wasn't a temporary, local objects are implicitly treated as xvalues in return statements anyway. – ildjarn Jul 06 '12 at 19:27

2 Answers2

33

You don't want a managed pointer to grab ownership of a raw pointer implicitly, as that could end up in undefined behavior. Consider a function void f( int * ); and a call int * p = new int(5); f(p); delete p;. Now imagine that someone refactors f to take a managed pointer (of any type) and that implicit conversions were allowed: void f( std::unique_ptr<int> p ); if the implicit conversion is allowed, your code will compile but cause undefined behavior.

In the same way consider that the pointer might not be even dynamically allocated: int x = 5; f( &x );...

Acquisition of ownership is a important enough operation that it is better having it explicit: the programmer (and not the compiler) knows whether the resource should be managed through a smart pointer or not.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 4
    +1, esp for "Acquisition of ownership is a important enough operation that it is better having it explicit." – Macke Jul 06 '12 at 19:15
  • "_Acquisition of ownership is a important enough operation that it is better having it explicit_" Good point. – curiousguy Aug 19 '12 at 05:49
20

Short answer:

The explicit constructor makes it difficult to write dangerous code. In other words, implicit constructor helps you to write dangerous code more easily.

Long answer:

If the constructor is implicit , then you could write such code easily:

void f(std::unique_ptr<int> param)
{
     //code

} //param will be destructed here, i.e when it goes out of scope
  //the pointer which it manages will be destructed as well. 

Now see the dangerous part:

int *ptr = new int;

f(ptr); 
//note that calling f is allowed if it is allowed:
//std::unique_ptr<int> test = new int;
//it is as if ptr is assigned to the parameter:
//std::unique_ptr<int> test = ptr;

//DANGER
*ptr = 10; //undefined behavior because ptr has been deleted by the unique_ptr!

Please read the comments. It explains each part of the code snippet above.

When calling f() with a raw pointer, the programmer may not realize that the parameter type of f() is std::unique_ptr which will take ownership of the pointer and will delete it when it goes out of scope. Programmer on the other hand may use it, and delete it without even realizing that it has already been deleted! This all happens because of implicit conversion from raw pointer to std::unique_ptr.

Note that std::shared_ptr has explicit constructor for the very same reason.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • How is this less risky: `void f(int *param) { std::unique_ptr smart(param); }` or `void f(int *param) { delete param; }`? – curiousguy Aug 10 '12 at 04:20
  • @curiousguy: You obviously didn't understand my answer. `std::unique_ptr smart(param);` is **explicit**, while my answer talks about the risk due to **implicit conversion** from `int*` to `std::unique_ptr`. – Nawaz Aug 10 '12 at 05:33
  • You obviously didn't understand my comment. How is explicit less risky than implicit conversions? – curiousguy Aug 10 '12 at 22:42
  • @curiousguy: Know the meaning of "risky" first. then know the meaning of "explicit" and "implicit". Then you will understand my answer yourself. – Nawaz Aug 11 '12 at 01:08
  • So, you think I don't understand "risky"? – curiousguy Aug 11 '12 at 01:22
  • @curiousguy: Alright, then please teach me why the language allows you to make constructor `explicit` (if you want to). What is the advantage of making constructor `explicit`? Why the `explicit` keyword exists? – Nawaz Aug 11 '12 at 06:50
  • I will try. We do not want conversions (implicit or explicit) between unrelated concepts. For example, we do not want an implicit conversion from `float` to `string`, because they represent different concepts, with no *obvious and canonical* path between the two. A constructor has no name (which is a C++ design error IMO), and a one argument constructor was always an implicit conversion in the first C++ design, and this was soon recognised as an issue. The situation of C++ WRT explicit conversions is messy: a one-argument constructor creates an explicit conversion; this is not wanted. – curiousguy Aug 17 '12 at 01:11
  • (...) The usual example of one-argument constructor which conceptually does not represents a conversion is `array(int)`: an `int` is not "morally converted" (implicitly or explicitly), it is just a way to call the constructor. The fact that the constructor happens to have one argument should not make a difference here, but it does. C++ functional style cast is IMO one of the worst design error of C++ (with access control handling). – curiousguy Aug 17 '12 at 01:11
  • (...) With have to live with that, and an explicit constructor does not protect us as much as we would like: `static_cast(1)` is still possible, we do not have non-converting constructors in C++ because **`array(1)` is a conversion** (a cast to type `array`). – curiousguy Aug 17 '12 at 01:12
  • @curiousguy: I will quote two statements from your comment which I didn't find clear enough. First you said, `(1)` *"We do not want conversions **(implicit or explicit)** between unrelated concepts."* (please note emphasized words). then you said, `(2)` *"For example, we do not want an **implicit** conversion from float to string, because they represent different concepts, ..."* . But does it mean that we don't want "explicit" conversion from `float` to `string` as well? If we want it, then why do we want "explicit" but not "implicit"? Doesn't it make your statetement `(1)` wrong? – Nawaz Aug 17 '12 at 05:29
  • I don't want to be able to write `string(1.)`. I don't want to read that in any C++ program, as I wouldn't know what the intent of the programmer was. OTOH, a specifically named function, like `to_string`, intended to convert its argument to its textual representation, is another issue. I don't view that `to_string` as a conversion function, and would rather name it `to_text`. – curiousguy Aug 17 '12 at 19:10
  • "_If we want it, then why do we want "explicit" but not "implicit"?_" I don't want any user defined function to define an "explicit conversion" (a C style cast). But the C++ design implies that **a one argument constructor is actually an explicit conversion**. I don't like that, but I have to live with that. `static_cast >(p)` is unwanted (at least to me), at least as much as `shared_ptr sp = p;` is unwanted. I just don't want to taking ownership as a particular explicit conversion. – curiousguy Aug 17 '12 at 19:15
  • **A conversion (implicit or explicit) should just convert, it should be pure** (not have side effects). `static_cast(x);` should be side effect free. `static_cast >(p);` has a notable side effect: `delete p;` It means that **smart pointers are pathological objects**. To me the issue is not explicit vs. implicit, it is conversion constructor vs. ordinary constructor. Obviously, implicit conversions are more likely to be invoked accidentally than explicit conversions. – curiousguy Aug 17 '12 at 19:23
  • But a design error less likely to provoke programming errors than another design error is still a design error. ***Modelling as a conversion something which is not a conversion at all is a design error.** The construction of a `unique_ptr` from a raw pointer is not modelling a conversion. Some one argument constructors model a conversion: floating point to complex type, `const char*` to string type, `char` to string type (constructing a string with just one character). Here "complex" and "string" refer to some UDT with such constructors. – curiousguy Aug 17 '12 at 19:27
  • "_`std::unique_ptr smart(param);` is explicit_" It is a direct initialization, not an explicit conversion and `std::unique_ptr smart = param;` is not an implicit conversion attempt either. I don't think that `std::unique_ptr smart(param);` is significantly less likely to hide programming errors than `std::unique_ptr smart = param;`. **Both do not make very explicit that another `delete` call is implied** if `param` is an `int*`, but a previously implied `delete` call is delayed if `param` is a `std::unique_ptr`. – curiousguy Aug 17 '12 at 19:37
  • @curiousguy: First, I'm finding your comments dogmatic, because all you're saying *"I don't want this; I don't like that"* without giving any rationale for your dislikes. Second, `to_string` *is* an **explicit conversion function** irrespective of what you think of it, and changing its name to `to_text` wouldn't change the fact that it is an "explicit" conversion function (start a topic if you want to know others opinion). Third, `std::unique_ptr smart(param);` IS explicit conversion. ...[contd.] – Nawaz Aug 18 '12 at 04:47
  • ...[contd.] Fourth, as you finally **admitted** : *"Obviously, implicit conversions are **more likely to be invoked accidentally** than explicit conversions."* Exactly, that is what I'm saying in my answer. Thank you. ;-) – Nawaz Aug 18 '12 at 04:48
  • "_Third, std::unique_ptr smart(param); IS explicit conversion._" According to what? Who is "dogmatic" now? – curiousguy Aug 18 '12 at 07:24
  • @curiousguy: Why don't you read your own comment? ***"But the C++ design implies that a one argument constructor is actually an explicit conversion."*** So it is according to you too, *"std::unique_ptr smart(param); IS explicit conversion"*. :-) – Nawaz Aug 18 '12 at 07:28
  • @curiousguy: Anyway, all I wanted to hear this from you : ***"Obviously, implicit conversions are more likely to be invoked accidentally than explicit conversions."*** So I think we should stop here. – Nawaz Aug 18 '12 at 07:30
  • "_Why don't you read your own comment? _" Why don't you read them? "_So it is according to you too, "`std::unique_ptr smart(param);` IS explicit conversion". :-)_" No, I never wrote that. You could as well argue that `complex c; c = 1.;` is an explicit conversion. (You could also argue that any implicit conversion is an explicit conversion.) – curiousguy Aug 18 '12 at 13:55
  • "_Second, `to_string` is an explicit conversion function irrespective of what you think of it,_" In your own dialect, perhaps. In C++, no. – curiousguy Aug 18 '12 at 13:56
  • @curiousguy: *"No, I never wrote that."*. You did write ***"But the C++ design implies that a one argument constructor is actually an explicit conversion."*** (although it is not entirely correct; it is correct only if the constructor is declared with the keyword `explicit`, otherwise it is an implicit conversion function). And if `to_string()` is not a conversion function, then what it is? what does it *do* if it doesn't *convert* the argument to `string`? – Nawaz Aug 18 '12 at 14:01
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15507/discussion-between-curiousguy-and-nawaz) – curiousguy Aug 18 '12 at 14:16
  • also with an explicit constructor one can write easily `void f(std::unique_ptr param) {}` the difference is on how you can call the function – 463035818_is_not_an_ai Jul 05 '21 at 19:06