23

I have a class Foo

class Foo;

a factory returning a pointer to it:

std::unique_ptr<Foo> createFoo(); 

and, since I have been told by Herb that a plain function with no special claim on the lifetime of Foo should take plain naked pointers:

void plainf(Foo* f);

How is my client supposed to correctly do this?

plainF(createFoo());

He would not be happy if he had to write:

auto someName = createFoo();
plainF(someName.get()); 
jimifiki
  • 5,377
  • 2
  • 34
  • 60
  • 2
    I tell you, don't use naked pointers (yes, you can quote me on that ;) ) – hellow Jun 14 '18 at 13:06
  • 4
    @hellow please read the "Herb" link. It's illuminating. Naked pointers are fine indeed. – jimifiki Jun 14 '18 at 13:15
  • 6
    Well, they are fine in a controlled environment where every programmer knows that every programmer knows than a naked pointer means an observer pointer. I'd play safe and use a reference or an optional reference. – YSC Jun 14 '18 at 13:17
  • @jimifiki Sure, they are fine, but see `YSC` comment. You can use raw pointers everywhere and do not use smart pointer at all. Why should one, raw pointers are fine, aren't they? – hellow Jun 14 '18 at 13:31
  • 2
    @YSC: [std::experimental::observer_ptr](https://en.cppreference.com/w/cpp/experimental/observer_ptr/observer_ptr) to be sure. :) and *"There are no optional references; a program is ill-formed if it instantiates an optional with a reference type"* :-( – Jarod42 Jun 14 '18 at 13:34
  • @hellow IMO smart pointers are precious. And I agree with YSC on raw pointers: "they are fine in a controlled environment". – jimifiki Jun 14 '18 at 13:57
  • 1
    @Jarod42 I'm ashamed :/ yes, observer_ptr is nice and all, but it's experimental since the last dinosaur died... – YSC Jun 14 '18 at 14:27
  • @Ron: as `void plainf(Foo* f);` doesn't take ownership, you would create memory leak. – Jarod42 Jun 14 '18 at 14:29

2 Answers2

39

You can use the get member function which returns a raw pointer to the owned object.

plainF(createFoo().get());

The temporary created by createFoo() will not go out of scope until plainF has finished. So long as plainF doesn't pass the pointer up out of scope this is completely safe.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @NathanOliver Right, thanks! Is it still meaningful to say then that `plainF` has "no claim on the lifetime of Foo", when it's actually responsible for not passing the argument "up out of scope"? – lubgr Jun 14 '18 at 13:12
  • 8
    @lubgr Passing the pointer up is basically playing with the lifetime. Passing the pointer up out of scope would be extending it's lifetime and since it does not own the pointer it should not be making lifetime decisions on it. – NathanOliver Jun 14 '18 at 13:17
21

If you don't need the argument to plainF to be nullable, you can also change its signature to take the argument by reference, allowing for a slightly less verbose syntax:

std::unique_ptr<Foo> createFoo(); 
void plainf(Foo& f);

plainF(*createFoo());

All the lifetime considerations mentioned in NathanOliver's answer apply, so this is just as safe to use.

Note that this implies that createFoo will never return an empty unique_ptr. So if that function is allowed to fail, you must indicate that error through other means, eg. by throwing an exception.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Assuming you are not terminating the program on a bad allocation, you can't make a `noexcept` factory anyway (outside of some specific cases where you own the memory you allocated from and don't use standard `new`). – meneldal Jun 15 '18 at 04:51