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::optional
s of const references, since they cannot be assigned to.
To indicate optional arguments, Square brackets are commonly used, and can also be used to group parameters that must be specified together. To indicate required arguments, Angled brackets are commonly used, following the same grouping conventions as square brackets.
C does not support optional parameters.
Optional arguments enable you to omit arguments for some parameters. Both techniques can be used with methods, indexers, constructors, and delegates. When you use named and optional arguments, the arguments are evaluated in the order in which they appear in the argument list, not the parameter list.
Optional arguments are generally not allowed in C (but they exist in C++ and in Ocaml, etc...). The only exception is variadic functions (like printf ).
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).
Does function overloading provide a clean solution here? E.g. To declare both the const ref and empty param list versions of the function?
This may depend on what the function body does in the no argument/null case - and how you can manage the two implementations to minimize code overlap.
Raw pointers are usually fine for this type of optional argument passing, actually one of the only times it is fine to use raw pointers overall. This is also the canonical recommended way.
That being said, boost::optional
does allow you to use reference optional and const reference optionals. It was decided against to have this feature in the std
library (for reasons I leave out here).
This is actually what std::reference_wrapper was made for. Also see Does it make sense to combine optional with reference_wrapper? for more reasoning as to when to use it, and when not to use it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With