23

From what I understand there are 2* ways you can implement a function that sometimes doesnt return a result(for example is person found in a list of ppl).

*- we ignore raw ptr version, pair with a bool flag, and exception when none found version.

boost::optional<Person> findPersonInList();

or

std::unique_ptr<Person> findPersonInList();

So are there any reasons to prefere one over the other?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

7 Answers7

26

It depends: do you wish to return a handle or a copy.

If you wish to return a handle:

  • Person*
  • boost::optional<Person&>

are both acceptable choices. I tend to use a Ptr<Person> class which throws in case of null access, but that's my paranoia.

If you wish to return a copy:

  • boost::optional<Person> for non polymorphic classes
  • std::unique_ptr<Person> for polymorphic classes

because dynamic allocation incurs an overhead, so you only use it when necessary.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 4
    Don't use pointers and you won't have to throw on null access (you should almost never have null pointers). – Cat Plus Plus Jan 16 '13 at 14:48
  • 4
    @CatPlusPlus: So, you would rather throw `NotFound` than `NullPointer` ? What is the difference ? – Matthieu M. Jan 16 '13 at 14:52
  • 5
    @MatthieuM. [Good answer] In reply to your comment: `NotFound` is clearly preferred, once again because it expresses the semantic more directly. However, I think throwing in this context is not a good option anyway. – Konrad Rudolph Jan 16 '13 at 14:53
  • 1
    @KonradRudolph: I tend to use the null pointer alternative because it's then up to the client to decide whether to check explicitly or rely on the exception. If the client expect potential nullity, then they better check explicitly (an `if` with early return is less bulky than nesting `try/catch`) and if they do not expect it (it does not make sense this time) they are still protected. – Matthieu M. Jan 16 '13 at 14:56
  • regarding overhead, how is boost::optional implemented, is it just a bool + "contained stuff" ? . I know it has member functions, Im talking about mem layout – NoSenseEtAl Jan 16 '13 at 15:13
  • @NoSenseEtAl: it's packed (entirely stack-allocated), though I don't know the exact details. I imagine that `std::aligned_storage, bool` is as good as anything else. – Matthieu M. Jan 16 '13 at 15:15
  • 1
    @MatthieuM. hahaha you made that mistake that *everyone* makes: it's `aligned_storage<...>::type` ;) – R. Martinho Fernandes Jan 16 '13 at 15:27
  • 1
    @R.MartinhoFernandes: damned! :p – Matthieu M. Jan 16 '13 at 15:47
  • I agree that the dynamic allocation required for `unique_ptr` is usually a disadvantage but couldn't it also be an advantage? E.g if `Person` is large and often not found then use of `unique_ptr` would often avoid allocating a large object. – Chris Drew Aug 01 '15 at 04:58
  • @ChrisDrew: You mean avoiding allocating a large object *on the stack* (to avoid stackoverflow)? It's a very rare corner case, and you'll find yourself in either of two situations: either the object is big, so you allocate it on the heap by yourself, and in this case it's already handled by a smart pointer OR the object is handled through a by-value wrapper (which internally manages the heap allocation) and `optional` is a natural fit. – Matthieu M. Aug 01 '15 at 12:57
  • @MatthieuM No, I mean if you are expecting to return a lot of empty objects then a null `unique_ptr` is just the size of a pointer, an empty `optional` is at least as large as a `Person`. – Chris Drew Aug 01 '15 at 13:11
  • @ChrisDrew: I am afraid I fail to understand the issue. I would need you to clarify why you care about the size of the return type; at the programming language level it does not matter, so I can guess it's an implementation issue, but it appears insufficient to answer your question. – Matthieu M. Aug 01 '15 at 15:56
  • Why the distinction between polymorphic and non-polymorphic classes? – phant0m May 25 '16 at 07:43
  • @phant0m: You cannot store any derived class of `Base` in a `Base` value, you can only store it behind a pointer, because their size is unknown. – Matthieu M. May 25 '16 at 09:51
  • @MatthieuM. Ah I see, thanks! I didn't realize the `optional` actually stores the value itself. – phant0m May 25 '16 at 11:28
14

The general answer is that your intentions are expressed by boost::optional and not by std::unique_ptr. That said, your special case of a find operation should probably conform to the standard library way of doing it, assuming that your underlying type has a concept of iterators: return an iterator to end() if no element is found, and an iterator to the element otherwise.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
8

boost::optional more clearly states your intention. You need to document explicitly that empty std::unique_ptr means there's no value to return

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
  • 4
    I disagree, a null pointer (whether raw or smart) has signaled the absence of result for *decades*, it's ubiquituous. – Matthieu M. Jan 16 '13 at 14:42
  • 4
    @MatthieuM.: probably I explained it wrong. If a function returns a std::unique_ptr, it's not obvious that it can return no value, because similar function that always returns something would have the same signature – Andriy Tylychko Jan 16 '13 at 14:45
  • We will have to agree to disagree then, for me pointer always rhymes with possible nullity... but then I am paranoid. – Matthieu M. Jan 16 '13 at 14:47
  • 3
    @MatthieuM. “more clearly states your intent” is unambiguous. `boost::optional`’s *purpose* is to state this particular intent. So it’s – pretty much by definition – clearer. There’s nothing to disagree here, this is a factual argument. Furthermore, you forget about the inherent semantic of `unique_ptr`: it signals (exclusive) *ownership*. – Konrad Rudolph Jan 16 '13 at 14:47
  • 3
    @KonradRudolph: I agree `boost::optional` state the intent more clearly, but if you look at my answer you will see that there are potential issues with it. It's meant to contain a value, not a dynamically allocated class, and therefore is not suitable for owned instances of polymorphic classes. *There* you use `std::unique_ptr`, and there is no point in wrapping it in `boost::optional` on top to express it might be null. – Matthieu M. Jan 16 '13 at 14:49
  • 4
    @MatthieuM as you say, a null pointer has signaled the absence of a result for decades - doesn't mean that it was a good idea though: [link](http://lambda-the-ultimate.org/node/3186) Tony Hoare / Historically Bad Ideas: "Null References: The Billion Dollar Mistake" Abstract: I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W) ... – Paul Delhanty Mar 26 '14 at 12:59
6

There is a fourth way of doing it : make the function throw an exception if there is nothing found.

I know this does not really answer your question and therefore I apologize but maybe you hadn't thought about it.

ibizaman
  • 3,053
  • 1
  • 23
  • 34
  • hi, I was planning to add that in * :) but still tnx. basically I dont like using ex as an message mechanism. :D – NoSenseEtAl Jan 16 '13 at 14:41
  • 1
    If the collection can just not have the element you're looking for, exceptions are *totally* not suitable for that. I can imagine looking for different phrases that don't get any results I'm looking for very fast; combined with exception overhead, looks like a catastrophe. – Bartek Banachewicz Jan 16 '13 at 14:53
  • 3
    For me the exception overhead is not at runtime, it's in the source code. try-catch introduces *two* separate scopes and you need gymnastics to even get anything out of them (i.e. you need to declare a variable out of the try-catch, so you can use it later; are you going to make everything default constructible?). – R. Martinho Fernandes Jan 16 '13 at 15:02
  • @R.MartinhoFernandes: who needs class invariants ? – Matthieu M. Jan 16 '13 at 15:12
5

Ah, Xeo didn't show up yet?

Well, I told you this once, and I'll tell it again: these two are completely different objects with different purposes.

  • unique_ptr means I own an object. It's just different way of saying "I am an object". Thus, null unique_ptr is something that can attract attention. I was expecting an object, but I got nothing; the code must be wrong!
  • optional means I can sometimes be not initialized, but that's ok. In this case, there's no worries; if it's None, it's the behavior that has been thought of.

The fact that both implicitly convert to bool doesn't mean they can be used interchargeably. Use optional with code that is likely not to produce any output (reading a stream, for example). Use unique_ptr in factory objects; they will most likely create an object for you, and if not, throw an exception.

A conclusion regarding your example : find should return optional.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • 2
    I don't understand this fear of null `unique_ptr`. There is nothing in the C++ standard that says a `unique_ptr` should not be null. And a null `unique_ptr` expresses "not found" to me perfectly. In just the same way `optional` does. The real advantage of `optional` in mind is that you don't have to pay the cost of a dynamic memory allocation like Matthieu says. – Chris Drew Aug 01 '15 at 04:31
3

Conceptually boils down to this, given the nullable requirement:

std::optional has value semantics, stack store.

std::unique_ptr has move semantics, heap store.

If you want value semantics and heap store, use std::indirect http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0201r1.pdf .

(If you want move semantics and stack store... I don't know. I guess it is a unique_ptr with a stack allocator?)

alfC
  • 14,261
  • 4
  • 67
  • 118
1

So are there any reasons to prefere one over the other?

They express very different intent: optional says that the function may not give a result to give you (and that is not an error case). unique_ptr tells you something about ownership semantics (and is more acceptable to use with null, to express an error).

Usually I would use the one that expresses best the intent behind the interface.

For example, consider that you were writing a HTTP server, that attempts to parse a received buffer into a HTTP request object. When you attempt to parse an incomplete buffer, there is no error case, you simply have to wait and buffer more data, then try again.

I would express this using optional,to make it clear that the function may not return anything (and not returning anything is not an error case).

In case my parsing had to validate stuff (e.g. a regex parser should yield an error if the parsed expression is invalid regex) I would return a null unique_ptr, or better yet, throw an exception.

utnapistim
  • 26,809
  • 3
  • 46
  • 82