string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable Declarationsstd::optional
parametersOriginally posted as TotW #163 on July 11, 2019
Updated 2020-04-06
Quicklink: abseil.io/tips/163
Are nulls really a billion-dollar mistake?
Let’s say you need to implement a function with a parameter that may or may not
exist. You might be tempted to use modern, fancy-schmancy std::optional
for
this. However, if the object is big enough that it should be passed by
reference, std::optional
is probably not what you want. Consider the following
two declarations:
void MyFunc(const std::optional<Foo>& foo); // May copy by value void MyFunc(std::optional<const Foo&> foo); // Doesn't compile
The first option probably doesn’t do what you want. If someone passes a
std::optional<Foo>
into MyFunc
, it is passed by reference, but if someone
passes a Foo
into MyFunc
(for example as a return value), the Foo
will be
copied by value into a temporary std::optional<Foo>
, which will then be
passed by reference into the function. If your goal was to avoid copying the
Foo
, you haven’t.
The second option would be great, but unfortunately is not supported by
std::optional
.
Avoid function parameters of the form const std::optional&
.
If your object is small enough to not need pass-by-reference, you should take
the object wrapped in an std::optional
by value. For example:
void MyFunc(std::optional<int> bar); void MyFunc(std::optional<absl::string_view> baz);
If you are intentionally making a copy of the argument, you should also accept
the std::optional
by value to make that clear:
void MyFunc(std::optional<Foo> foo);
Otherwise, skip the std::optional
altogether.
You can pass it by absl::Nullable<const Foo*>
and let nullptr
indicate “does
not exist.”
void MyFunc(absl::Nullable<const Foo*> foo);
This will be just as efficient as passing by const Foo&
, but supports null
values. See Tip #116 for more on when to use a pointer instead of a
const reference.
std::optional
for???std::optional
can be used if you own the thing that’s optional. For example,
class members and function return values often work well with std::optional
.
If you expect all callers of your function to already have a
std::optional<Foo>
and never pass in a Foo
, then you may take a const
std::optional<Foo>&
. However, this is rare; it usually only occurs if your
function is private within your own file/library.
std::reference_wrapper
?The documentation for std::optional
points out that you can use a
std::reference_wrapper
to work around the fact that optional references aren’t
supported:
void MyFunc(std::optional<std::reference_wrapper<const Foo>> foo);
However, we don’t recommend this:
std::reference_wrapper
has surprisingly subtle semantics, making it
difficult to understand and use safely. For instance, various utilities in
the standard library special case it in ways that make it act differently
from a normal value or reference.std::optional<std::reference_wrapper<const Foo>>
is cumbersome and
verbose, compared to absl::Nullable<const Foo*>
.