36

Obviously, std::optional is the best choice to return an optional value from a function if one uses C++17 or boost (see also GOTW #90)

std::optional<double> possiblyFailingCalculation()

But what and why would be the best alternative if one is stuck with an older version (and can't use boost)?

I see a few options:

  1. STL smart pointers (C++11 only)

    std::unique_ptr<double> possiblyFailingCalculation();
    
    • (+) virtually the same usage as optional
    • (−) confusing to have smart pointers to non-polymorphic types or built-in types
  2. Pairing it up with a bool

    std::pair<double,bool> possiblyFailingCalculation();
    
  3. Old style

    bool possiblyFailingCalculation(double& output);
    
    • (−) incompatible with new C++11 auto value = calculation() style
  4. A DIY template: a basic template with the same functionality is easy enough to code, but are there any pitfalls to implement a robust std::optional<T> look-a-like template ?

  5. Throw an exception

    • (−) Sometimes "impossible to calculate" is a valid return value.
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
mr_T
  • 2,571
  • 3
  • 22
  • 39
  • 8
    and another option - throw an exception, since your function failed. – David Haim Mar 16 '16 at 10:53
  • 2
    I've added the option, but sometimes one want exception-free functions and is "impossible to calculate" just a regular return value. E.g. you don't want the function Edge getLargestEdge(); in a class Triangle to throw an exception in case of an equilateral triangle. – mr_T Mar 16 '16 at 11:28
  • @DavidHaim: Post that as an answer; unless failure is a really common case, one would hope whatever overhead exception handling incurs would be fairly meaningless, and avoiding the risk of a caller not checking for failure and getting silently incorrect results is a laudable goal. – ShadowRanger Mar 16 '16 at 11:30
  • @mr_T: Devil's advocate: In an equilateral triangle, all edges are the largest edge. :-) – ShadowRanger Mar 16 '16 at 11:30
  • 2
    @ShadowRanger: If opinion questions were on topic for this site, and David answered "throw an exception" as the best alternative to returning `std::optional`, I would certainly downvote that, seeing as it completely changes the semantics. – Benjamin Lindley Mar 16 '16 at 11:34
  • 1
    @mr_T A better example might be how `std::map` lookup could conceptually return an `optional>`, returning `none` instead of `end()` on lookup failure - which is a reasonable, non-exceptional outcome. – Barry Mar 16 '16 at 11:43
  • There are apparently many pitfalls when you try to implement `std::optional`. On the other hand, you can just copy-paste one of the many free implementations you can find all around the web. – Morwenn Mar 16 '16 at 15:00
  • Since you're using `double`, would `NaN` be an alternative? – edmz Mar 16 '16 at 15:51

3 Answers3

25

std::optional, like its boost::optional parent, is a pretty basic class template. It's a bool, some storage, and a bunch of convenience member functions most of which are one line of code and an assert.

The DIY option is definitely preferred. (1) involves allocation and (2), (3) involve having to construct a T even if you want a null value - which doesn't matter at all for double but does matter for more expensive types. With (5), exceptions are not a replacement for optional.

You can always compare your implementation to Boost's. It's a small header-only library, after all.

Barry
  • 286,269
  • 29
  • 621
  • 977
3

I'd also consider a sentinel value.

In the case of a double the NaN value (std::numeric_limits<double>::quiet_NaN()) is a possible candidate (only meaningful if std::numeric_limits<double>::has_quiet_NaN == true).

There are various opinions about this approach (e.g. take a look at NaN or false as double precision return value and Good sentinel value for double if prefer to use -ffast-math).

In specific domains there could be other meaningful sentinel values.

In any case (not only for double) I'd adopt/implement something like markable (https://github.com/akrzemi1/markable) to avoid magic values and indicate that the value may not be there and that its potential absence should be checked by the user.

For additional motivation and overview of this approach: Efficient optional values.

manlio
  • 18,345
  • 14
  • 76
  • 126
3

instead of std::optional, use tl::optional from this link: https://github.com/TartanLlama/optional

It has the same public interface as its std counterpart, only it compiles in C++98 also.

I used it in production code (C++11) and works great!

Grim Fandango
  • 2,296
  • 1
  • 19
  • 27