Recently I had a chance to take a look at Rust. When returning from a function Rust uses std::option to return either class A in success or class B in exception.
And I found out something similar in C++, namely std::optional. Most of C++ users argued that "why use std::optional when we can fully make use of null pointers?" According to C++ Committee, it was due to minimize human errors. Then we can ask one thing: what kind of errors, then?
Let's take a look at the code below:
There's nothing special in this code. Since the class Insider can be used optionally, it can be allocated to heap. When anti_memory_leak is removed from memory, Insider object will be also removed in destructor so we have means for memory leak. We proved that we can do it without std::optional.
...... Did we?
Then let's investigate the code below:
This function crashed in the last line. Why? The reason is in the one line above. When you call vector1.push_back(), even though you use std::move() object1 is destructed and recreated. And when destructing, the destructor of anti_memory_leak is called, and it surely remove insider from the heap. In other words, vector1.back().insider becomes a dangling pointer. It's kind of unfortunate, the result is the same if you use emplace_back() instead of push_back(). Anyway the application crash.
Now is the time std::optional should be used. If you declare an object with std::optional, the memory is initialized without allocating that optional object, and it is initialized when the optional object is explicitly created. Of course there's a small overhead, but say, it's also same for other similar(?) classes like std::shared_ptr. We have raw pointers, but to manage our precious heap more safely, we can automate some of the management so that we can solve potential incidents(including both memory leak and dangling pointer) more easily.
If we refactor the code above using std::optional, it will be like this:
If the flow of the code is simple it won't be a big problem. However, if the flow becomes anyway compilcated(e.g. multithreading), there should be chances to free memory or miss the chance when we have to, regardless of my intention. Let's think of std::optional as some kind of insurance policy; though we all agree that insurance fee is somewhat "waste of money"(lol), but we spend money to prepare for the worst? I think it's the same for std::optional.