mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
Add absl::optional_ref<T>
PiperOrigin-RevId: 872459397 Change-Id: Ib2a3265c46c1ceca31190f5d4722bde06b59eeb4
This commit is contained in:
committed by
Copybara-Service
parent
5088cf5194
commit
a62029e346
@@ -453,6 +453,7 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"types/any.h"
|
||||
"types/compare.h"
|
||||
"types/optional.h"
|
||||
"types/optional_ref.h"
|
||||
"types/span.h"
|
||||
"types/internal/span.h"
|
||||
"types/variant.h"
|
||||
|
||||
@@ -164,3 +164,29 @@ cc_test(
|
||||
"@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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -154,6 +154,34 @@ absl_cc_test(
|
||||
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(
|
||||
|
||||
294
absl/types/optional_ref.h
Normal file
294
absl/types/optional_ref.h
Normal file
@@ -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_
|
||||
370
absl/types/optional_ref_test.cc
Normal file
370
absl/types/optional_ref_test.cc
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user