Add absl::optional_ref<T> PiperOrigin-RevId: 872459397 Change-Id: Ib2a3265c46c1ceca31190f5d4722bde06b59eeb4
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 0fa06af..60a4ec2 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -453,6 +453,7 @@ "types/any.h" "types/compare.h" "types/optional.h" + "types/optional_ref.h" "types/span.h" "types/internal/span.h" "types/variant.h"
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index cac5e4b..a52c358 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -164,3 +164,29 @@ "@googletest//:gtest_main", ], ) + +cc_library( + name = "optional_ref", + hdrs = ["optional_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "optional_ref_test", + size = "small", + srcs = ["optional_ref_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":optional_ref", + "//absl/base:config", + "//absl/log", + "//absl/strings", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +)
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 20a7e90..75dd07d 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -154,6 +154,34 @@ GTest::gmock_main ) +absl_cc_library( + NAME + internal_optional_ref + HDRS + "optional_ref.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + PUBLIC +) + +absl_cc_test( + NAME + optional_ref_test + SRCS + "optional_ref_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::internal_optional_ref + absl::strings + GTest::gmock_main +) + # Deprecated empty library. # Clients should remove this dependency. absl_cc_library(
diff --git a/absl/types/optional_ref.h b/absl/types/optional_ref.h new file mode 100644 index 0000000..fb21333 --- /dev/null +++ b/absl/types/optional_ref.h
@@ -0,0 +1,294 @@ +// Copyright 2026 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: optional_ref.h +// ----------------------------------------------------------------------------- +// +// `optional_ref<T>` provides a `std::optional`-like interface around `T*`. +// It is similar to C++26's `std::optional<T&>`, but with slight enhancements, +// such as the fact that it permits construction from rvalues. That is, it +// relaxes the std::reference_constructs_from_temporary constraint. Its intent +// is to make it easier for functions to accept nullable object addresses, +// regardless of whether or not they point to temporaries. +// +// It can be constructed in the following ways: +// * optional_ref<T> ref; +// * optional_ref<T> ref = std::nullopt; +// * T foo; optional_ref<T> ref = foo; +// * std::optional<T> foo; optional_ref<T> ref = foo; +// * T* foo = ...; optional_ref<T> ref = foo; +// * optional_ref<T> foo; optional_ref<const T> ref = foo; +// +// Since it is trivially copyable and destructible, it should be passed by +// value. +// +// Other properties: +// * Assignment is not allowed. Example: +// optional_ref<int> ref; +// // Compile error. +// ref = 2; +// +// * operator bool() is intentionally not defined, as it would be error prone +// for optional_ref<bool>. +// +// Example usage, assuming some type `T` that is expensive to copy: +// void ProcessT(optional_ref<const T> input) { +// if (!input.has_value()) { +// // Handle empty case. +// return; +// } +// const T& val = *input; +// // Do something with val. +// } +// +// ProcessT(std::nullopt); +// ProcessT(BuildT()); + +#ifndef ABSL_TYPES_OPTIONAL_REF_H_ +#define ABSL_TYPES_OPTIONAL_REF_H_ + +#include <cstddef> +#include <memory> +#include <optional> +#include <type_traits> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template <typename T> +class optional_ref { + template <typename U> + using EnableIfConvertibleFrom = + std::enable_if_t<std::is_convertible_v<U*, T*>>; + + public: + using value_type = T; + + constexpr optional_ref() : ptr_(nullptr) {} + constexpr optional_ref( // NOLINT(google-explicit-constructor) + std::nullopt_t) + : ptr_(nullptr) {} + + // Constructor given a concrete value. + constexpr optional_ref( // NOLINT(google-explicit-constructor) + T& input ABSL_ATTRIBUTE_LIFETIME_BOUND) + : ptr_(std::addressof(input)) {} + + // Constructors given an existing std::optional value. + // Templated on the input optional's type to avoid creating a temporary. + template <typename U, typename = EnableIfConvertibleFrom<const U>> + constexpr optional_ref( // NOLINT(google-explicit-constructor) + const std::optional<U>& input ABSL_ATTRIBUTE_LIFETIME_BOUND) + : ptr_(input.has_value() ? std::addressof(*input) : nullptr) {} + template <typename U, typename = EnableIfConvertibleFrom<U>> + constexpr optional_ref( // NOLINT(google-explicit-constructor) + std::optional<U>& input ABSL_ATTRIBUTE_LIFETIME_BOUND) + : ptr_(input.has_value() ? std::addressof(*input) : nullptr) {} + + // Constructor given a T*, where nullptr indicates empty/absent. + constexpr optional_ref( // NOLINT(google-explicit-constructor) + T* input ABSL_ATTRIBUTE_LIFETIME_BOUND) + : ptr_(input) {} + + // Don't allow naked nullptr as input, as this creates confusion in the case + // of optional_ref<T*>. Use std::nullopt instead to create an empty + // optional_ref. + constexpr optional_ref( // NOLINT(google-explicit-constructor) + std::nullptr_t) = delete; + + // Copying is allowed. + optional_ref(const optional_ref<T>&) = default; + // Assignment is not allowed. + optional_ref<T>& operator=(const optional_ref<T>&) = delete; + + // Conversion from optional_ref<U> is allowed iff U* is convertible to T*. + // (Note this also allows non-const to const conversions.) + template <typename U, typename = EnableIfConvertibleFrom<U>> + constexpr optional_ref( // NOLINT(google-explicit-constructor) + optional_ref<U> input) + : ptr_(input.as_pointer()) {} + + // Determines whether the `optional_ref` contains a value. Returns `false` if + // and only if `*this` is empty. + constexpr bool has_value() const { return ptr_ != nullptr; } + + // Returns a reference to an `optional_ref`s underlying value. The constness + // and lvalue/rvalue-ness of the `optional_ref` is preserved to the view of + // the `T` sub-object. Throws the same error as `std::optional`'s `value()` + // when the `optional_ref` is empty. + constexpr T& value() const { + return ABSL_PREDICT_TRUE(ptr_ != nullptr) + ? *ptr_ + // Replicate the same error logic as in `std::optional`'s + // `value()`. It either throws an exception or aborts the + // program. We intentionally ignore the return value of + // the constructed optional's value as we only need to run + // the code for error checking. + : ((void)std::optional<T>().value(), *ptr_); + } + + // Returns the value iff *this has a value, otherwise returns `default_value`. + template <typename U> + constexpr T value_or(U&& default_value) const { + // Instantiate std::optional<T>::value_or(U) to trigger its static_asserts. + if (false) { + // We use `std::add_const_t` here since just using `const` makes MSVC + // complain about the syntax. + (void)std::add_const_t<std::optional<T>>{}.value_or( + std::forward<U>(default_value)); + } + return ptr_ != nullptr ? *ptr_ + : static_cast<T>(std::forward<U>(default_value)); + } + + // Accesses the underlying `T` value of an `optional_ref`. If the + // `optional_ref` is empty, behavior is undefined. + constexpr T& operator*() const { + ABSL_HARDENING_ASSERT(ptr_ != nullptr); + return *ptr_; + } + constexpr T* operator->() const { + ABSL_HARDENING_ASSERT(ptr_ != nullptr); + return ptr_; + } + + // Convenience function to represent the `optional_ref` as a `T*` pointer. + constexpr T* as_pointer() const { return ptr_; } + // Convenience function to represent the `optional_ref` as an `optional`, + // which incurs a copy when the `optional_ref` is non-empty. The template type + // allows for implicit type conversion; example: + // optional_ref<std::string> a = ...; + // std::optional<std::string_view> b = a.as_optional<std::string_view>(); + template <typename U = std::decay_t<T>> + constexpr std::optional<U> as_optional() const { + if (ptr_ == nullptr) return std::nullopt; + return *ptr_; + } + + private: + T* const ptr_; + + // T constraint checks. You can't have an optional of nullopt_t or + // in_place_t. + static_assert(!std::is_same_v<std::nullopt_t, std::remove_cv_t<T>>, + "optional_ref<nullopt_t> is not allowed."); + static_assert(!std::is_same_v<std::in_place_t, std::remove_cv_t<T>>, + "optional_ref<in_place_t> is not allowed."); +}; + +// Template type deduction guides: + +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>; + +namespace optional_ref_internal { + +// This is a C++-11 compatible version of std::equality_comparable_with that +// only requires `t == u` is a valid boolean expression. +// +// We still need this for a couple reasons: +// - As of 2026-02-13, Abseil supports C++17. +// - Even for targets that are built with the default toolchain, using +// std::equality_comparable_with gives us an error due to mutual recursion +// between its definition and our definition of operator==. +// +template <typename T, typename U> +using enable_if_equality_comparable_t = std::enable_if_t<std::is_convertible_v< + decltype(std::declval<T>() == std::declval<U>()), bool>>; + +} // namespace optional_ref_internal + +// Compare an optional referenced value to std::nullopt. + +template <typename T> +constexpr bool operator==(optional_ref<T> a, std::nullopt_t) { + return !a.has_value(); +} +template <typename T> +constexpr bool operator==(std::nullopt_t, optional_ref<T> b) { + return !b.has_value(); +} +template <typename T> +constexpr bool operator!=(optional_ref<T> a, std::nullopt_t) { + return a.has_value(); +} +template <typename T> +constexpr bool operator!=(std::nullopt_t, optional_ref<T> b) { + return b.has_value(); +} + +// Compare two optional referenced values. Note, this does not test that the +// contained `ptr_`s are equal. If the caller wants "shallow" reference equality +// semantics, they should use `as_pointer()` explicitly. + +template <typename T, typename U> +constexpr bool operator==(optional_ref<T> a, optional_ref<U> b) { + return a.has_value() ? *a == b : !b.has_value(); +} + +// Compare an optional referenced value to a non-optional value. + +template < + typename T, typename U, + typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>> +constexpr bool operator==(const T& a, optional_ref<U> b) { + return b.has_value() && a == *b; +} +template < + typename T, typename U, + typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>> +constexpr bool operator==(optional_ref<T> a, const U& b) { + return b == a; +} + +// Inequality operators, as above. + +template <typename T, typename U> +constexpr bool operator!=(optional_ref<T> a, optional_ref<U> b) { + return !(a == b); +} +template < + typename T, typename U, + typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>> +constexpr bool operator!=(optional_ref<T> a, const U& b) { + return !(a == b); +} +template < + typename T, typename U, + typename = optional_ref_internal::enable_if_equality_comparable_t<T, U>> +constexpr bool operator!=(const T& a, optional_ref<U> b) { + return !(a == b); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_OPTIONAL_REF_H_
diff --git a/absl/types/optional_ref_test.cc b/absl/types/optional_ref_test.cc new file mode 100644 index 0000000..dda1624 --- /dev/null +++ b/absl/types/optional_ref_test.cc
@@ -0,0 +1,370 @@ +// Copyright 2026 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/types/optional_ref.h" + +#include <cstddef> +#include <memory> +#include <optional> +#include <string> +#include <string_view> +#include <type_traits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +using ::testing::Optional; +using ::testing::Pointee; + +TEST(OptionalRefTest, SimpleType) { + int val = 5; + optional_ref<int> ref = optional_ref(val); + optional_ref<int> empty_ref = std::nullopt; + EXPECT_THAT(ref, Optional(5)); + EXPECT_TRUE(ref.has_value()); + EXPECT_EQ(*ref, val); + EXPECT_EQ(ref.value(), val); + EXPECT_EQ(ref, ref); + EXPECT_EQ(ref, val); + EXPECT_EQ(val, ref); + EXPECT_NE(ref, empty_ref); + EXPECT_NE(empty_ref, ref); +} + +TEST(OptionalRefTest, SimpleConstType) { + const int val = 5; + optional_ref<const int> ref = optional_ref(val); + EXPECT_THAT(ref, Optional(5)); +} + +TEST(OptionalRefTest, DefaultConstructed) { + optional_ref<int> ref; + EXPECT_EQ(ref, std::nullopt); + EXPECT_EQ(std::nullopt, ref); +} + +TEST(OptionalRefTest, EmptyOptional) { + auto ref = optional_ref<int>(std::nullopt); + EXPECT_EQ(ref, std::nullopt); + EXPECT_EQ(std::nullopt, ref); +} + +TEST(OptionalRefTest, OptionalType) { + const std::optional<int> val = 5; + optional_ref<const int> ref = val; + EXPECT_THAT(ref, Optional(5)); + EXPECT_EQ(ref.as_pointer(), &*val); + + const std::optional<int> empty; + optional_ref<const int> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); + + // Cannot make non-const reference to const optional. + static_assert( + !std::is_convertible_v<const std::optional<int>&, optional_ref<int>>); +} + +TEST(OptionalRefTest, NonConstOptionalType) { + std::optional<int> val = 5; + optional_ref<int> ref = val; + EXPECT_THAT(ref, Optional(5)); + EXPECT_EQ(ref.as_pointer(), &*val); + + std::optional<int> empty; + optional_ref<int> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); +} + +TEST(OptionalRefTest, NonConstOptionalTypeToConstRef) { + std::optional<int> val = 5; + optional_ref<const int> ref = val; + EXPECT_THAT(ref, Optional(5)); + EXPECT_EQ(ref.as_pointer(), &*val); + + std::optional<int> empty; + optional_ref<const int> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); +} + +TEST(OptionalRefTest, NonConstOptionalWithConstValueType) { + std::optional<const int> val = 5; + optional_ref<const int> ref = val; + EXPECT_THAT(ref, Optional(5)); + EXPECT_EQ(ref.as_pointer(), &*val); + + std::optional<const int> empty; + optional_ref<const int> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); + + // Not possible to convert to non-const reference. + static_assert( + !std::is_convertible_v<std::optional<const int>&, optional_ref<int>>); +} + +class TestInterface {}; +class TestDerivedClass : public TestInterface {}; + +TEST(OptionalRefTest, BaseDerivedConvertibleOptionalType) { + const std::optional<TestDerivedClass> dc = TestDerivedClass{}; + optional_ref<const TestInterface> ref = dc; + EXPECT_NE(ref, std::nullopt); + EXPECT_EQ(ref.as_pointer(), &*dc); + + const std::optional<TestDerivedClass> empty; + optional_ref<const TestInterface> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<const std::optional<TestInterface>&, + optional_ref<const TestDerivedClass>>); + static_assert(!std::is_convertible_v<const std::optional<TestDerivedClass>&, + optional_ref<TestInterface>>); +} + +TEST(OptionalRefTest, NonConstBaseDerivedConvertibleOptionalType) { + std::optional<TestDerivedClass> dc = TestDerivedClass{}; + optional_ref<TestInterface> ref = dc; + EXPECT_NE(ref, std::nullopt); + EXPECT_EQ(ref.as_pointer(), &*dc); + + std::optional<TestDerivedClass> empty; + optional_ref<TestInterface> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<std::optional<TestInterface>&, + optional_ref<TestDerivedClass>>); +} + +TEST(OptionalRefTest, NonConstBaseDerivedConvertibleOptionalTypeToConstRef) { + std::optional<TestDerivedClass> dc = TestDerivedClass{}; + optional_ref<const TestInterface> ref = dc; + EXPECT_NE(ref, std::nullopt); + EXPECT_EQ(ref.as_pointer(), &*dc); + + std::optional<TestDerivedClass> empty; + optional_ref<const TestInterface> empty_ref = empty; + EXPECT_EQ(empty_ref, std::nullopt); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<std::optional<TestInterface>&, + optional_ref<const TestDerivedClass>>); + static_assert(!std::is_convertible_v<const std::optional<TestInterface>&, + optional_ref<const TestDerivedClass>>); +} + +TEST(OptionalRefTest, PointerCtor) { + int val = 5; + optional_ref<const int> ref = &val; + EXPECT_THAT(ref, Optional(5)); + + auto auto_ref = optional_ref(&val); + static_assert(std::is_same_v<decltype(auto_ref), optional_ref<int>>, + "optional_ref(T*) should deduce to optional_ref<T>."); + EXPECT_THAT(auto_ref, Optional(5)); + + int* foo = nullptr; + optional_ref<const int> empty_ref = foo; + EXPECT_EQ(empty_ref, std::nullopt); + + optional_ref<int*> ptr_ref = foo; + EXPECT_THAT(ptr_ref, Optional(nullptr)); + static_assert( + !std::is_constructible_v<optional_ref<int*>, std::nullptr_t>, + "optional_ref should not be constructible with std::nullptr_t."); + + // Pointer polymorphism works. + TestDerivedClass dc; + optional_ref<const TestInterface> dc_ref = &dc; + EXPECT_NE(dc_ref, std::nullopt); +} + +TEST(OptionalRefTest, ValueDeathWhenEmpty) { + optional_ref<int> ref; +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(ref.value(), std::bad_optional_access); +#else + EXPECT_DEATH_IF_SUPPORTED(ref.value(), ""); +#endif +} + +TEST(OptionalRefTest, ImplicitCtor) { + const int val = 5; + optional_ref<const int> ref = val; + EXPECT_THAT(ref, Optional(5)); +} + +TEST(OptionalRefTest, DoesNotCopy) { + // Non-copyable type. + auto val = std::make_unique<int>(5); + optional_ref<std::unique_ptr<int>> ref = optional_ref(val); + EXPECT_THAT(ref, Optional(Pointee(5))); +} + +TEST(OptionalRefTest, DoesNotCopyConst) { + // Non-copyable type. + const auto val = std::make_unique<int>(5); + optional_ref<const std::unique_ptr<int>> ref = optional_ref(val); + EXPECT_THAT(ref, Optional(Pointee(5))); +} + +TEST(OptionalRefTest, RefCopyable) { + auto val = std::make_unique<int>(5); + optional_ref<std::unique_ptr<int>> ref = optional_ref(val); + optional_ref<std::unique_ptr<int>> copy = ref; + EXPECT_THAT(copy, Optional(Pointee(5))); +} + +TEST(OptionalRefTest, ConstConvertible) { + auto val = std::make_unique<int>(5); + optional_ref<std::unique_ptr<int>> ref = optional_ref(val); + optional_ref<const std::unique_ptr<int>> converted = ref; + EXPECT_THAT(converted, Optional(Pointee(5))); + EXPECT_EQ(converted.as_pointer(), &val); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<optional_ref<const std::unique_ptr<int>>, + optional_ref<std::unique_ptr<int>>>); +} + +TEST(OptionalRefTest, BaseDerivedConvertible) { + TestDerivedClass dc; + optional_ref<TestDerivedClass> dc_ref = dc; + optional_ref<TestInterface> converted = dc_ref; + EXPECT_NE(converted, std::nullopt); + EXPECT_EQ(converted.as_pointer(), &dc); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<optional_ref<TestInterface>, + optional_ref<TestDerivedClass>>); +} + +TEST(OptionalRefTest, BaseDerivedConstConvertible) { + TestDerivedClass dc; + optional_ref<TestDerivedClass> dc_ref = dc; + optional_ref<const TestInterface> converted = dc_ref; + EXPECT_NE(converted, std::nullopt); + EXPECT_EQ(converted.as_pointer(), &dc); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<optional_ref<const TestInterface>, + optional_ref<TestDerivedClass>>); + static_assert(!std::is_convertible_v<optional_ref<const TestDerivedClass>, + optional_ref<TestInterface>>); +} + +TEST(OptionalRefTest, BaseDerivedBothConstConvertible) { + TestDerivedClass dc; + optional_ref<const TestDerivedClass> dc_ref = dc; + optional_ref<const TestInterface> converted = dc_ref; + EXPECT_NE(converted, std::nullopt); + EXPECT_EQ(converted.as_pointer(), &dc); + + // Not possible in the other direction. + static_assert(!std::is_convertible_v<optional_ref<const TestInterface>, + optional_ref<const TestDerivedClass>>); +} + +TEST(OptionalRefTest, TriviallyCopyable) { + static_assert( + std::is_trivially_copyable_v<optional_ref<std::unique_ptr<int>>>); +} + +TEST(OptionalRefTest, TriviallyDestructible) { + static_assert( + std::is_trivially_destructible_v<optional_ref<std::unique_ptr<int>>>); +} + +TEST(OptionalRefTest, RefNotAssignable) { + static_assert(!std::is_copy_assignable_v<optional_ref<int>>); + static_assert(!std::is_move_assignable_v<optional_ref<int>>); +} + +struct TestStructWithCopy { + TestStructWithCopy() = default; + TestStructWithCopy(TestStructWithCopy&&) { + LOG(FATAL) << "Move constructor should not be called"; + } + TestStructWithCopy(const TestStructWithCopy&) { + LOG(FATAL) << "Copy constructor should not be called"; + } + TestStructWithCopy& operator=(const TestStructWithCopy&) { + LOG(FATAL) << "Assign operator should not be called"; + } +}; + +TEST(OptionalRefTest, DoesNotCopyUsingFatalCopyAssignOps) { + TestStructWithCopy val; + optional_ref<TestStructWithCopy> ref = optional_ref(val); + EXPECT_NE(ref, std::nullopt); + EXPECT_NE(optional_ref(TestStructWithCopy{}), std::nullopt); +} + +std::string AddExclamation(optional_ref<const std::string> input) { + if (!input.has_value()) { + return ""; + } + return absl::StrCat(*input, "!"); +} + +TEST(OptionalRefTest, RefAsFunctionParameter) { + EXPECT_EQ(AddExclamation(std::nullopt), ""); + EXPECT_EQ(AddExclamation(std::string("abc")), "abc!"); + std::string s = "def"; + EXPECT_EQ(AddExclamation(s), "def!"); + EXPECT_EQ(AddExclamation(std::make_optional<std::string>(s)), "def!"); +} + +TEST(OptionalRefTest, ValueOrWhenHasValue) { + std::optional<int> val = 5; + EXPECT_EQ(optional_ref(val).value_or(2), 5); +} + +TEST(OptionalRefTest, ValueOrWhenEmpty) { + std::optional<int> val = std::nullopt; + EXPECT_EQ(optional_ref(val).value_or(2), 2); +} + +TEST(OptionalRefTest, AsOptional) { + EXPECT_EQ(optional_ref<int>().as_optional(), std::nullopt); + std::string val = "foo"; + optional_ref<const std::string> ref = val; + static_assert( + std::is_same_v<decltype(ref.as_optional()), std::optional<std::string>>, + "The type parameter of optional_ref should decay by default for the " + "return type in as_optional()."); + std::optional<std::string> opt_string = ref.as_optional(); + EXPECT_THAT(opt_string, Optional(val)); + + std::optional<std::string_view> opt_view = + ref.as_optional<std::string_view>(); + EXPECT_THAT(opt_view, Optional(val)); +} + +TEST(OptionalRefTest, Constexpr) { + static constexpr int foo = 123; + constexpr optional_ref<const int> ref(foo); + static_assert(ref.has_value() && *ref == foo && ref.value() == foo, ""); +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl