chromium/base/types/optional_ref.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TYPES_OPTIONAL_REF_H_
#define BASE_TYPES_OPTIONAL_REF_H_

#include <memory>
#include <optional>
#include <type_traits>

#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"

namespace base {

// `optional_ref<T>` is similar to `std::optional<T>`, except it does not own
// the underlying value.
//
// When passing an optional parameter, prefer `optional_ref` to `const
// std::optional<T>&` as the latter often results in hidden copies due to
// implicit conversions, e.g. given the function:
//
//   void TakesOptionalString(const std::optional<std::string>& str);
//
// And a call to that looks like:
//
//   std::string s = "Hello world!";
//   TakesOptionalString(s);
//
// This copies `s` into a temporary `std::optional<std::string>` in order to
// call `TakesOptionalString()`.
//
// The C++ style guide recommends using `const T*` instead of `const
// std::optional<T>&` when `T` would normally be passed by reference. However
// `const T*` is not always a good substitute because:
//
// - `const T*` disallows the use of temporaries, since it is not possible to
//   take the address of a temporary.
// - additional boilerplate (e.g. `OptionalToPtr`) is required to pass an
//   `std::optional<T>` to a `const T*` function parameter.
//
// Like `span<T>`, mutability of `optional_ref<T>` is controlled by the template
// argument `T`; e.g. `optional_ref<const int>` only allows const access to the
// referenced `int` value.
//
// Thus, `optional_ref<const T>` can be constructed from:
// - `std::nullopt`
// - `const T*` or `T*`
// - `const T&` or `T&`
// ` `const std::optional<T>&` or `std::optional<T>&`
//
// While `optional_ref<T>` can only be constructed from:
// - `std::nullopt`
// - `T*`
// - `T&`
// - `std::optional<T>&`
//
// Implicit conversions are disallowed, e.g. this will not compile:
//
//   [](base::optional_ref<std::string> s) {}("Hello world!");
//
// This restriction may be relaxed in the future if it proves too onerous.
//
// `optional_ref<T>` is lightweight and should be passed by value. It is copy
// constructible but not copy assignable, to reduce the risk of lifetime bugs.
template <typename T>
class optional_ref {};

template <typename T>
optional_ref(const T&) -> optional_ref<const T>;
template <typename T>
optional_ref(T&) -> optional_ref<T>;

template <typename T>
optional_ref(const std::optional<T>&) -> optional_ref<const T>;
template <typename T>
optional_ref(std::optional<T>&) -> optional_ref<T>;

template <typename T>
optional_ref(T*) -> optional_ref<T>;

template <typename T>
constexpr bool operator==(std::nullopt_t, optional_ref<T> x) {}

template <typename T>
constexpr bool operator==(optional_ref<T> x, std::nullopt_t) {}

}  // namespace base

#endif  // BASE_TYPES_OPTIONAL_REF_H_