4

What's "wrong" with this code, for a simple-minded example?

unique_ptr<char> meow = strdup("meow");

Whether or not I provide the "deleter" argument to the unique_ptr template, a unique_ptr<T> cannot be assigned from T*.

Why wouldn't <memory> offer this seemingly intuitive shortcut? Is it simply an oversight, or would such assignability be a fundamentally bad idea for some reason?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • 1
    it should be: `std::unique_ptr meow{strdup("meow")};` – Marek R Feb 25 '21 at 16:35
  • Does this answer your question? [unique\_ptr operator=](https://stackoverflow.com/questions/9393874/unique-ptr-operator) – underscore_d Feb 25 '21 at 16:36
  • 3
    *"Why wouldn't offer this seemingly intuitive shortcut?"* - Precisely that. It's simple and intuitive to the point it's easy to (ab)use without understanding the ownership repercussions. Who doesn't like groking a MLOC codebase to find a potential wrong use of `=`? Even `auto_ptr` never offered that. – StoryTeller - Unslander Monica Feb 25 '21 at 16:36
  • 2
    Note, that even if `meow = strdup()` would work, you have to use custom deleter that calls `free()`, instead of calling `delete`. – KamilCuk Feb 25 '21 at 16:44

1 Answers1

8

Why wouldn't <memory> offer this seemingly intuitive shortcut?

Imagine you have

int bar;
{
    int * foo = &bar;
    std::unique_ptr<int> uptr = foo;
    // use uptr
}

When uptr goes out of scope, it's going to try and do delete pointer;, which will try to call delete on memory that was not allocated by new. That's undefined behavior and can cause all sorts of problems. Instead of allowing buggy code like that to be able to be written, the standard disallows it.

If you are really sure you want to construct a unique_ptr from an existing pointer, and you know the default deleter is what you want, then you can use the form of

auto pointer_name = std::unique_ptr<type>(pointer_i_know_needs_to_be_deleted);
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @NicolBolas IIRC, `std::unique_ptr` doesn't off CTAD as you can't differentiate `unique_ptr(new int);` from `unique_ptr(new int[10]);` – NathanOliver Feb 25 '21 at 16:44
  • I can imagine _lots_ of other programmer-mistakes -- including an explicit freeing of the pointer given to a `unique_ptr` earlier. The construct you propose works, but, dang, it cannot be cast into `T*` -- nor even into `const T*` – Mikhail T. Feb 25 '21 at 16:44
  • 1
    @MikhailT. - Yes it can. You call `get()`, and you get a `T*`. – StoryTeller - Unslander Monica Feb 25 '21 at 16:46
  • 1
    @MikhailT. What do you mean by *it cannot be cast into `T*` -- nor even into `const T*`* ? Are you talking about passing a `unique_ptr` to a function that takes a raw pointer? If so, then you need to use `get` to get a raw pointer from it. Pointer are hard to get right. `unique_ptr` was designed to try and limit the mistakes you could do with it, unlike a raw pointer. – NathanOliver Feb 25 '21 at 16:47
  • I mean `const char *c = pointer_name;` does not work, @NathanOliver – Mikhail T. Feb 25 '21 at 16:50
  • @MikhailT. You need `const char *c = unique_pointer_name.get();` – NathanOliver Feb 25 '21 at 16:52
  • Thanks, @NathanOliver, Monica already pointed `get()` out -- but that's still more verbose and harder to read. Why wouldn't a cast to `const T*` be provided -- calling the `get()` underneath? A separate question, I suppose... Sigh... – Mikhail T. Feb 25 '21 at 16:55
  • 1
    @MikhailT. over time implicit conversions have been seen to be more dangerous (time to fix bugs) than making the programmer type a few more letters. The conversion operator(s) can get involved in unexpected conversions leading to hard to diagnose bugs. To follow up you could research why (for example) `std::string` has `c_str()` rather than an implicit conversion operator. – Richard Critten Feb 25 '21 at 16:59
  • @MikhailT. Pointers are probably the most abused and understood part of C++. They are hard to get right even for people with experience. Instead of keeping that status quo interface, `unique_ptr` adds some verbosity to make sure that what you are doing is really what you want. IMHO, I don't mind paying that cost. Especially if I don't have to use `new` anymore. – NathanOliver Feb 25 '21 at 17:08
  • @MikhailT. - Then you weren't talking about a cast. A cast is by definition an **explicit conversion**. The standard does not offer many *implicit* conversions for its containers or utilities. And it's a *good* thing. – StoryTeller - Unslander Monica Feb 25 '21 at 17:17
  • _making the programmer type a few more letters_ seems like we've come a full circle since exactly the opposite -- allowing shorter and more readable code -- was touted as C++'s advantage over C: "Hey, look, you can even change what _operators_ and _casts_ mean, is not that wonderful?" – Mikhail T. Feb 25 '21 at 17:25
  • @MikhailT. this post is addressing implicit conversion constructors but most of the points also apply to implicit conversion operators (note this is from 11 years ago) https://stackoverflow.com/questions/2346083/why-implicit-conversion-is-harmful-in-c And also https://stackoverflow.com/questions/11146354/is-it-dangerous-to-have-a-cast-operator-on-a-unique-ptr 1st answer where the implicit returned pointer is left dangling – Richard Critten Feb 25 '21 at 17:38