-3

I have the following code:

  msg_buf_ptr = std::make_unique<QByteArray>();

  return QDataStream{msg_buf_ptr, QIODevice::WriteOnly};

I am getting the following error:

no known conversion for argument 1 from ‘std::unique_ptr<QByteArray>’ to ‘QByteArray*’

But...why? I thought unique_ptr and shared_ptr automatically degrade to raw pointers when passed as arguments to functions taking pointers. If not, why not? If they (usually) do, why does this fail in the case of QByteArray?

I could explicitly call msg_buf_ptr.get(), but that seems like it should be unnecessary.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • 4
    Why would they convert to `T *`? Most implicit conversions end up being more trouble than they're worth. – Praetorian Mar 09 '15 at 21:35
  • @Praetorian Well, because they're *pointers*. At least, that's the way I tend to think, even if it's not how the standards committee thinks. Oh well. – Kyle Strand Mar 09 '15 at 21:37
  • 2
    That's the point. It's `unique_ptr`. Unique. If it gets implicitly converted to pointer when you pass it somewhere, it would loose the whole purpose of being unique, because the function can store it inside or do something else and BAM, it's not unique anymore. Same goes for `shared_ptr`. The whole point is to have underlying pointers obtained by `get()` exist for s short time and not pass them anywhere or something like this. – JustSomeGuy Mar 09 '15 at 21:44
  • 2
    I'd assume that explicit use of `get()` forces you to know what you are doing. For example, it means that you consciously get a naked pointer to memory which is owned by the `unique_ptr`. – bialpio Mar 09 '15 at 21:45
  • @Spo1ler Nnnno, it does *not* lose the "purpose of being unique"; the purpose is *ownership*, and implicit conversion ensures that *the caller maintains ownership*, just as desired. If the function writer wishes to store (i.e. take partial ownership of) it, then the function signature should take a *smart pointer* rather than a raw pointer. – Kyle Strand Mar 09 '15 at 21:47
  • @Spo1ler See: http://youtu.be/xnqTKD8uD64?t=12m10s – Kyle Strand Mar 09 '15 at 21:49
  • 1
    Anyway non-trivial implicit conversions are bad and should be avoided in any design. – JustSomeGuy Mar 09 '15 at 21:53
  • @Spo1ler I (personally) would consider degrading in the case of a function call "trivial", but of course that relies on the knowledge that the function designer recognizing that a raw pointer as an argument indicates that the function will *not* take ownership of the pointer. – Kyle Strand Mar 09 '15 at 22:16
  • 2
    That's the point. You can't make a general-purpose smart-pointers and have them implicitly convert back to raw pointers, because there are different models of pointer ownership across existing code base. It forces you to explicitly `get()` the raw pointer so that you take a minute and consider the consequences of passing a raw pointer. What if you pass it to third-party code, that assumes that it has ownership? Or some other assumption which will break crash your program in run-time without a single compile-time warning. – JustSomeGuy Mar 09 '15 at 22:22
  • @Spo1ler If I am using a `unique_ptr`, I have ownership. If the third-party code assumes it has ownership, and I don't know that, then there is literally *no way* for me to avoid undefined behavior, because whether or not I'm using a smart pointer, I will be deleting the objects that I think I own. `.get()` does not offer protection from this--or, as far as I can tell, from any other danger. – Kyle Strand Mar 09 '15 at 22:29
  • 1
    It isn't suppose to protect you. Just make you think twice about actually passing your smart pointer as raw pointer. – JustSomeGuy Mar 09 '15 at 22:38
  • @Spo1ler Again, if I *don't already know* that the function takes ownership, *thinking twice* won't save me. – Kyle Strand Mar 09 '15 at 23:07
  • And if I *do* know that it does *not* take ownership, then `.get()` is nothing but an inconvenience. – Kyle Strand Mar 09 '15 at 23:08
  • Do you really want `std::shared_ptr sp = /* ... */; std::unique_ptr up(sp);` to compile and silently trigger UB at run time? – T.C. Mar 10 '15 at 07:05
  • @T.C. Certainly not, but if my suggestion were implemented, I'd also expect an overload that throws a `static_assert` (or possibly just a `=delete`d overload) on the `unique_ptr` constructor that would take a `shared_ptr`. – Kyle Strand Mar 10 '15 at 15:52
  • @KyleStrand When you ask "why", what do you mean by that? I mean can it meaningfully be answered by anyone but STL's authors themselves? Why did you write STL that way, instead of some other way? Because otherwise you'd be better off firing an email to them than asking it on a public forum hoping they'll stumble upon your question by chance and answer it. – sashoalm Mar 11 '15 at 12:47
  • @sashoalm When I originally asked this, I had sort of expected that QByteArray was a special case, in which case the answer would be something like "this doesn't work because the Qt developers disabled it by....." Since QByteArray is not a special case, I've gone ahead and answered my own question. – Kyle Strand Mar 11 '15 at 17:01

1 Answers1

1

No, this is not a special case; the standard-library smart pointers do not degrade implicitly in contexts requiring raw pointers.

As mentioned in the question, the proper way to access the underlying raw pointer from a unique_ptr is to use get(). This is a design feature, apparently intended to help avoid accidentally causing multiple-ownership scenarios, which would lead to undefined behavior.

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167