Search code examples
c++pointersoption-typesemanticsoptional-parameters

Modern C++ approach for providing optional arguments


Let's take the following function declaration:

void print(SomeType const* i);

Here, the const* nature of the argument i suggest the intent, that the parameter is optional, since it may be nullptr. If this was not intended, the argument would instead just be a const&. Communicating optional-semantics were certainly not the original intent for designing pointers, but using them to do so happens to work just fine for a long time.

Now, since using raw pointers is generally discouraged in modern C++ (and should be avoided in favor of std::unique_ptr and std::shared_ptr to precisely indicate particular ownership-semantics), I wonder how to properly indicate function parameters' optional-semantics without passing by value, i. e. copying, as

void print(std::optional<SomeType> i);

would do.

After thinking about it for a while I came up with the idea of using:

void print(std::optional<SomeType const&> i);

This would in fact be most precise. But it turns out that std::optional cannot have reference types

Also, using

void print(std::optional<SomeType> const& i);

would in no way be optimal, since then we would require our SomeType to exists in an std::optional on the caller-side, again possibly (or rather likely) requiring a copy there.

Question: So what would be a nice modern approach for allowing optional arguments without copying? Is using a raw pointer here still a reasonable approach in modern C++?


¹: Ironically the depicted reason for why std::optional cannot have reference types (controversy about rebinding or forwarding on assignment) does not apply in the case of std::optionals of const references, since they cannot be assigned to.


Solution

  • Accepting a raw pointer is perfectly fine and is still done in plenty of "modern" codebases (which I'll note is a fast-moving target). Just put a comment on the function saying that it's allowed to be null and whether the function holds a copy of the pointer after the call (i.e. what are the lifetime requirements for the pointed-to value).