// Copyright 2017 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.h" // This test is a no-op when absl::optional is an alias for std::optional. #if !defined(ABSL_USES_STD_OPTIONAL) #include #include #include #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/log/log.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #if defined(__cplusplus) && __cplusplus >= 202002L // In C++20, volatile-qualified return types are deprecated. #define ABSL_VOLATILE_RETURN_TYPES_DEPRECATED 1 #endif // The following types help test an internal compiler error in GCC5 though // GCC10. The case OptionalTest.InternalCompilerErrorInGcc5ToGcc10 crashes the // compiler without a workaround. This test case should remain at the beginning // of the file as the internal compiler error is sensitive to other constructs // in this file. template using GccIceHelper1 = T; template struct GccIceHelper2 {}; template class GccIce { template &, U>> GccIce& operator=(GccIceHelper2 const&) {} }; TEST(OptionalTest, InternalCompilerErrorInGcc5ToGcc10) { GccIce instantiate_ice_with_same_type_as_optional; static_cast(instantiate_ice_with_same_type_as_optional); absl::optional val1; absl::optional val2; val1 = val2; } struct Hashable {}; namespace std { template <> struct hash { size_t operator()(const Hashable&) { return 0; } }; } // namespace std struct NonHashable {}; namespace { std::string TypeQuals(std::string&) { return "&"; } std::string TypeQuals(std::string&&) { return "&&"; } std::string TypeQuals(const std::string&) { return "c&"; } std::string TypeQuals(const std::string&&) { return "c&&"; } struct StructorListener { int construct0 = 0; int construct1 = 0; int construct2 = 0; int listinit = 0; int copy = 0; int move = 0; int copy_assign = 0; int move_assign = 0; int destruct = 0; int volatile_copy = 0; int volatile_move = 0; int volatile_copy_assign = 0; int volatile_move_assign = 0; }; // Suppress MSVC warnings. // 4521: multiple copy constructors specified // 4522: multiple assignment operators specified // We wrote multiple of them to test that the correct overloads are selected. #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4521) #pragma warning(disable : 4522) #endif struct Listenable { static StructorListener* listener; Listenable() { ++listener->construct0; } explicit Listenable(int /*unused*/) { ++listener->construct1; } Listenable(int /*unused*/, int /*unused*/) { ++listener->construct2; } Listenable(std::initializer_list /*unused*/) { ++listener->listinit; } Listenable(const Listenable& /*unused*/) { ++listener->copy; } Listenable(const volatile Listenable& /*unused*/) { ++listener->volatile_copy; } Listenable(volatile Listenable&& /*unused*/) { ++listener->volatile_move; } Listenable(Listenable&& /*unused*/) { ++listener->move; } Listenable& operator=(const Listenable& /*unused*/) { ++listener->copy_assign; return *this; } Listenable& operator=(Listenable&& /*unused*/) { ++listener->move_assign; return *this; } // use void return type instead of volatile T& to work around GCC warning // when the assignment's returned reference is ignored. void operator=(const volatile Listenable& /*unused*/) volatile { ++listener->volatile_copy_assign; } void operator=(volatile Listenable&& /*unused*/) volatile { ++listener->volatile_move_assign; } ~Listenable() { ++listener->destruct; } }; #ifdef _MSC_VER #pragma warning(pop) #endif StructorListener* Listenable::listener = nullptr; struct ConstexprType { enum CtorTypes { kCtorDefault, kCtorInt, kCtorInitializerList, kCtorConstChar }; constexpr ConstexprType() : x(kCtorDefault) {} constexpr explicit ConstexprType(int i) : x(kCtorInt) {} constexpr ConstexprType(std::initializer_list il) : x(kCtorInitializerList) {} constexpr ConstexprType(const char*) // NOLINT(runtime/explicit) : x(kCtorConstChar) {} int x; }; struct Copyable { Copyable() {} Copyable(const Copyable&) {} Copyable& operator=(const Copyable&) { return *this; } }; struct MoveableThrow { MoveableThrow() {} MoveableThrow(MoveableThrow&&) {} MoveableThrow& operator=(MoveableThrow&&) { return *this; } }; struct MoveableNoThrow { MoveableNoThrow() {} MoveableNoThrow(MoveableNoThrow&&) noexcept {} MoveableNoThrow& operator=(MoveableNoThrow&&) noexcept { return *this; } }; struct NonMovable { NonMovable() {} NonMovable(const NonMovable&) = delete; NonMovable& operator=(const NonMovable&) = delete; NonMovable(NonMovable&&) = delete; NonMovable& operator=(NonMovable&&) = delete; }; struct NoDefault { NoDefault() = delete; NoDefault(const NoDefault&) {} NoDefault& operator=(const NoDefault&) { return *this; } }; struct ConvertsFromInPlaceT { ConvertsFromInPlaceT(absl::in_place_t) {} // NOLINT }; TEST(optionalTest, DefaultConstructor) { absl::optional empty; EXPECT_FALSE(empty); constexpr absl::optional cempty; static_assert(!cempty.has_value(), ""); EXPECT_TRUE( std::is_nothrow_default_constructible>::value); } TEST(optionalTest, nulloptConstructor) { absl::optional empty(absl::nullopt); EXPECT_FALSE(empty); constexpr absl::optional cempty{absl::nullopt}; static_assert(!cempty.has_value(), ""); EXPECT_TRUE((std::is_nothrow_constructible, absl::nullopt_t>::value)); } TEST(optionalTest, CopyConstructor) { { absl::optional empty, opt42 = 42; absl::optional empty_copy(empty); EXPECT_FALSE(empty_copy); absl::optional opt42_copy(opt42); EXPECT_TRUE(opt42_copy); EXPECT_EQ(42, *opt42_copy); } { absl::optional empty, opt42 = 42; absl::optional empty_copy(empty); EXPECT_FALSE(empty_copy); absl::optional opt42_copy(opt42); EXPECT_TRUE(opt42_copy); EXPECT_EQ(42, *opt42_copy); } #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { absl::optional empty, opt42 = 42; absl::optional empty_copy(empty); EXPECT_FALSE(empty_copy); absl::optional opt42_copy(opt42); EXPECT_TRUE(opt42_copy); EXPECT_EQ(42, *opt42_copy); } #endif // test copyablility EXPECT_TRUE(std::is_copy_constructible>::value); EXPECT_TRUE(std::is_copy_constructible>::value); EXPECT_FALSE( std::is_copy_constructible>::value); EXPECT_FALSE( std::is_copy_constructible>::value); EXPECT_FALSE(std::is_copy_constructible>::value); EXPECT_FALSE( absl::is_trivially_copy_constructible>::value); EXPECT_TRUE( absl::is_trivially_copy_constructible>::value); EXPECT_TRUE( absl::is_trivially_copy_constructible>::value); #if !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // See defect report "Trivial copy/move constructor for class with volatile // member" at // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094 // A class with non-static data member of volatile-qualified type should still // have a trivial copy constructor if the data member is trivial. // Also a cv-qualified scalar type should be trivially copyable. EXPECT_TRUE(absl::is_trivially_copy_constructible< absl::optional>::value); #endif // !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // constexpr copy constructor for trivially copyable types { constexpr absl::optional o1; constexpr absl::optional o2 = o1; static_assert(!o2, ""); } { constexpr absl::optional o1 = 42; constexpr absl::optional o2 = o1; static_assert(o2, ""); static_assert(*o2 == 42, ""); } { struct TrivialCopyable { constexpr TrivialCopyable() : x(0) {} constexpr explicit TrivialCopyable(int i) : x(i) {} int x; }; constexpr absl::optional o1(42); constexpr absl::optional o2 = o1; static_assert(o2, ""); static_assert((*o2).x == 42, ""); #ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG EXPECT_TRUE(absl::is_trivially_copy_constructible< absl::optional>::value); EXPECT_TRUE(absl::is_trivially_copy_constructible< absl::optional>::value); #endif #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) EXPECT_FALSE(std::is_copy_constructible< absl::optional>::value); #endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) } } TEST(optionalTest, MoveConstructor) { absl::optional empty, opt42 = 42; absl::optional empty_move(std::move(empty)); EXPECT_FALSE(empty_move); absl::optional opt42_move(std::move(opt42)); EXPECT_TRUE(opt42_move); EXPECT_EQ(42, opt42_move); // test movability EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE( std::is_move_constructible>::value); EXPECT_FALSE(std::is_move_constructible>::value); // test noexcept EXPECT_TRUE(std::is_nothrow_move_constructible>::value); EXPECT_EQ( absl::default_allocator_is_nothrow::value, std::is_nothrow_move_constructible>::value); EXPECT_TRUE(std::is_nothrow_move_constructible< absl::optional>::value); } TEST(optionalTest, Destructor) { struct Trivial {}; struct NonTrivial { NonTrivial(const NonTrivial&) {} NonTrivial& operator=(const NonTrivial&) { return *this; } ~NonTrivial() {} }; EXPECT_TRUE(std::is_trivially_destructible>::value); EXPECT_TRUE(std::is_trivially_destructible>::value); EXPECT_FALSE( std::is_trivially_destructible>::value); } TEST(optionalTest, InPlaceConstructor) { constexpr absl::optional opt0{absl::in_place_t()}; static_assert(opt0, ""); static_assert((*opt0).x == ConstexprType::kCtorDefault, ""); constexpr absl::optional opt1{absl::in_place_t(), 1}; static_assert(opt1, ""); static_assert((*opt1).x == ConstexprType::kCtorInt, ""); constexpr absl::optional opt2{absl::in_place_t(), {1, 2}}; static_assert(opt2, ""); static_assert((*opt2).x == ConstexprType::kCtorInitializerList, ""); EXPECT_FALSE((std::is_constructible, absl::in_place_t>::value)); EXPECT_FALSE((std::is_constructible, const absl::in_place_t&>::value)); EXPECT_TRUE( (std::is_constructible, absl::in_place_t, absl::in_place_t>::value)); EXPECT_FALSE((std::is_constructible, absl::in_place_t>::value)); EXPECT_FALSE((std::is_constructible, absl::in_place_t&&>::value)); } // template optional(U&&); TEST(optionalTest, ValueConstructor) { constexpr absl::optional opt0(0); static_assert(opt0, ""); static_assert(*opt0 == 0, ""); EXPECT_TRUE((std::is_convertible>::value)); // Copy initialization ( = "abc") won't work due to optional(optional&&) // is not constexpr. Use list initialization instead. This invokes // absl::optional::absl::optional(U&&), with U = const char // (&) [4], which direct-initializes the ConstexprType value held by the // optional via ConstexprType::ConstexprType(const char*). constexpr absl::optional opt1 = {"abc"}; static_assert(opt1, ""); static_assert(ConstexprType::kCtorConstChar == (*opt1).x, ""); EXPECT_TRUE( (std::is_convertible>::value)); // direct initialization constexpr absl::optional opt2{2}; static_assert(opt2, ""); static_assert(ConstexprType::kCtorInt == (*opt2).x, ""); EXPECT_FALSE( (std::is_convertible>::value)); // this invokes absl::optional::optional(int&&) // NOTE: this has different behavior than assignment, e.g. // "opt3 = {};" clears the optional rather than setting the value to 0 // According to C++17 standard N4659 [over.ics.list] 16.3.3.1.5, (9.2)- "if // the initializer list has no elements, the implicit conversion is the // identity conversion", so `optional(int&&)` should be a better match than // `optional(optional&&)` which is a user-defined conversion. // Note: GCC 7 has a bug with this overload selection when compiled with // `-std=c++17`. #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 7 && \ __cplusplus == 201703L #define ABSL_GCC7_OVER_ICS_LIST_BUG 1 #endif #ifndef ABSL_GCC7_OVER_ICS_LIST_BUG constexpr absl::optional opt3({}); static_assert(opt3, ""); static_assert(*opt3 == 0, ""); #endif // this invokes the move constructor with a default constructed optional // because non-template function is a better match than template function. absl::optional opt4({}); EXPECT_FALSE(opt4); } struct Implicit {}; struct Explicit {}; struct Convert { Convert(const Implicit&) // NOLINT(runtime/explicit) : implicit(true), move(false) {} Convert(Implicit&&) // NOLINT(runtime/explicit) : implicit(true), move(true) {} explicit Convert(const Explicit&) : implicit(false), move(false) {} explicit Convert(Explicit&&) : implicit(false), move(true) {} bool implicit; bool move; }; struct ConvertFromOptional { ConvertFromOptional(const Implicit&) // NOLINT(runtime/explicit) : implicit(true), move(false), from_optional(false) {} ConvertFromOptional(Implicit&&) // NOLINT(runtime/explicit) : implicit(true), move(true), from_optional(false) {} ConvertFromOptional( const absl::optional&) // NOLINT(runtime/explicit) : implicit(true), move(false), from_optional(true) {} ConvertFromOptional(absl::optional&&) // NOLINT(runtime/explicit) : implicit(true), move(true), from_optional(true) {} explicit ConvertFromOptional(const Explicit&) : implicit(false), move(false), from_optional(false) {} explicit ConvertFromOptional(Explicit&&) : implicit(false), move(true), from_optional(false) {} explicit ConvertFromOptional(const absl::optional&) : implicit(false), move(false), from_optional(true) {} explicit ConvertFromOptional(absl::optional&&) : implicit(false), move(true), from_optional(true) {} bool implicit; bool move; bool from_optional; }; TEST(optionalTest, ConvertingConstructor) { absl::optional i_empty; absl::optional i(absl::in_place); absl::optional e_empty; absl::optional e(absl::in_place); { // implicitly constructing absl::optional from // absl::optional absl::optional empty = i_empty; EXPECT_FALSE(empty); absl::optional opt_copy = i; EXPECT_TRUE(opt_copy); EXPECT_TRUE(opt_copy->implicit); EXPECT_FALSE(opt_copy->move); absl::optional opt_move = absl::optional(absl::in_place); EXPECT_TRUE(opt_move); EXPECT_TRUE(opt_move->implicit); EXPECT_TRUE(opt_move->move); } { // explicitly constructing absl::optional from // absl::optional absl::optional empty(e_empty); EXPECT_FALSE(empty); absl::optional opt_copy(e); EXPECT_TRUE(opt_copy); EXPECT_FALSE(opt_copy->implicit); EXPECT_FALSE(opt_copy->move); EXPECT_FALSE((std::is_convertible&, absl::optional>::value)); absl::optional opt_move{absl::optional(absl::in_place)}; EXPECT_TRUE(opt_move); EXPECT_FALSE(opt_move->implicit); EXPECT_TRUE(opt_move->move); EXPECT_FALSE((std::is_convertible&&, absl::optional>::value)); } { // implicitly constructing absl::optional from // absl::optional via // ConvertFromOptional(absl::optional&&) check that // ConvertFromOptional(Implicit&&) is NOT called static_assert( std::is_convertible, absl::optional>::value, ""); absl::optional opt0 = i_empty; EXPECT_TRUE(opt0); EXPECT_TRUE(opt0->implicit); EXPECT_FALSE(opt0->move); EXPECT_TRUE(opt0->from_optional); absl::optional opt1 = absl::optional(); EXPECT_TRUE(opt1); EXPECT_TRUE(opt1->implicit); EXPECT_TRUE(opt1->move); EXPECT_TRUE(opt1->from_optional); } { // implicitly constructing absl::optional from // absl::optional via // ConvertFromOptional(absl::optional&&) check that // ConvertFromOptional(Explicit&&) is NOT called absl::optional opt0(e_empty); EXPECT_TRUE(opt0); EXPECT_FALSE(opt0->implicit); EXPECT_FALSE(opt0->move); EXPECT_TRUE(opt0->from_optional); EXPECT_FALSE( (std::is_convertible&, absl::optional>::value)); absl::optional opt1{absl::optional()}; EXPECT_TRUE(opt1); EXPECT_FALSE(opt1->implicit); EXPECT_TRUE(opt1->move); EXPECT_TRUE(opt1->from_optional); EXPECT_FALSE( (std::is_convertible&&, absl::optional>::value)); } } TEST(optionalTest, StructorBasic) { StructorListener listener; Listenable::listener = &listener; { absl::optional empty; EXPECT_FALSE(empty); absl::optional opt0(absl::in_place); EXPECT_TRUE(opt0); absl::optional opt1(absl::in_place, 1); EXPECT_TRUE(opt1); absl::optional opt2(absl::in_place, 1, 2); EXPECT_TRUE(opt2); } EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.construct1); EXPECT_EQ(1, listener.construct2); EXPECT_EQ(3, listener.destruct); } TEST(optionalTest, CopyMoveStructor) { StructorListener listener; Listenable::listener = &listener; absl::optional original(absl::in_place); EXPECT_EQ(1, listener.construct0); EXPECT_EQ(0, listener.copy); EXPECT_EQ(0, listener.move); absl::optional copy(original); EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.copy); EXPECT_EQ(0, listener.move); absl::optional move(std::move(original)); EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.copy); EXPECT_EQ(1, listener.move); } TEST(optionalTest, ListInit) { StructorListener listener; Listenable::listener = &listener; absl::optional listinit1(absl::in_place, {1}); absl::optional listinit2(absl::in_place, {1, 2}); EXPECT_EQ(2, listener.listinit); } TEST(optionalTest, AssignFromNullopt) { absl::optional opt(1); opt = absl::nullopt; EXPECT_FALSE(opt); StructorListener listener; Listenable::listener = &listener; absl::optional opt1(absl::in_place); opt1 = absl::nullopt; EXPECT_FALSE(opt1); EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.destruct); EXPECT_TRUE(( std::is_nothrow_assignable, absl::nullopt_t>::value)); EXPECT_TRUE((std::is_nothrow_assignable, absl::nullopt_t>::value)); } TEST(optionalTest, CopyAssignment) { const absl::optional empty, opt1 = 1, opt2 = 2; absl::optional empty_to_opt1, opt1_to_opt2, opt2_to_empty; EXPECT_FALSE(empty_to_opt1); empty_to_opt1 = empty; EXPECT_FALSE(empty_to_opt1); empty_to_opt1 = opt1; EXPECT_TRUE(empty_to_opt1); EXPECT_EQ(1, empty_to_opt1.value()); EXPECT_FALSE(opt1_to_opt2); opt1_to_opt2 = opt1; EXPECT_TRUE(opt1_to_opt2); EXPECT_EQ(1, opt1_to_opt2.value()); opt1_to_opt2 = opt2; EXPECT_TRUE(opt1_to_opt2); EXPECT_EQ(2, opt1_to_opt2.value()); EXPECT_FALSE(opt2_to_empty); opt2_to_empty = opt2; EXPECT_TRUE(opt2_to_empty); EXPECT_EQ(2, opt2_to_empty.value()); opt2_to_empty = empty; EXPECT_FALSE(opt2_to_empty); EXPECT_FALSE(absl::is_copy_assignable>::value); EXPECT_TRUE(absl::is_copy_assignable>::value); EXPECT_FALSE(absl::is_copy_assignable>::value); EXPECT_FALSE( absl::is_copy_assignable>::value); EXPECT_FALSE(absl::is_copy_assignable>::value); EXPECT_TRUE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_copy_assignable::value); struct Trivial { int i; }; struct NonTrivial { NonTrivial& operator=(const NonTrivial&) { return *this; } int i; }; EXPECT_TRUE(absl::is_trivially_copy_assignable::value); EXPECT_FALSE(absl::is_copy_assignable::value); EXPECT_FALSE(absl::is_copy_assignable::value); EXPECT_TRUE(absl::is_copy_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { StructorListener listener; Listenable::listener = &listener; absl::optional empty, set(absl::in_place); EXPECT_EQ(1, listener.construct0); absl::optional empty_to_empty, empty_to_set, set_to_empty(absl::in_place), set_to_set(absl::in_place); EXPECT_EQ(3, listener.construct0); empty_to_empty = empty; // no effect empty_to_set = set; // copy construct set_to_empty = empty; // destruct set_to_set = set; // copy assign EXPECT_EQ(1, listener.volatile_copy); EXPECT_EQ(0, listener.volatile_move); EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_copy_assign); } #endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) } TEST(optionalTest, MoveAssignment) { { StructorListener listener; Listenable::listener = &listener; absl::optional empty1, empty2, set1(absl::in_place), set2(absl::in_place); EXPECT_EQ(2, listener.construct0); absl::optional empty_to_empty, empty_to_set, set_to_empty(absl::in_place), set_to_set(absl::in_place); EXPECT_EQ(4, listener.construct0); empty_to_empty = std::move(empty1); empty_to_set = std::move(set1); set_to_empty = std::move(empty2); set_to_set = std::move(set2); EXPECT_EQ(0, listener.copy); EXPECT_EQ(1, listener.move); EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.move_assign); } #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { StructorListener listener; Listenable::listener = &listener; absl::optional empty1, empty2, set1(absl::in_place), set2(absl::in_place); EXPECT_EQ(2, listener.construct0); absl::optional empty_to_empty, empty_to_set, set_to_empty(absl::in_place), set_to_set(absl::in_place); EXPECT_EQ(4, listener.construct0); empty_to_empty = std::move(empty1); // no effect empty_to_set = std::move(set1); // move construct set_to_empty = std::move(empty2); // destruct set_to_set = std::move(set2); // move assign EXPECT_EQ(0, listener.volatile_copy); EXPECT_EQ(1, listener.volatile_move); EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_move_assign); } #endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) EXPECT_FALSE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_FALSE(absl::is_move_assignable>::value); EXPECT_FALSE( std::is_nothrow_move_assignable>::value); EXPECT_TRUE( std::is_nothrow_move_assignable>::value); } struct NoConvertToOptional { // disable implicit conversion from const NoConvertToOptional& // to absl::optional. NoConvertToOptional(const NoConvertToOptional&) = delete; }; struct CopyConvert { CopyConvert(const NoConvertToOptional&); CopyConvert& operator=(const CopyConvert&) = delete; CopyConvert& operator=(const NoConvertToOptional&); }; struct CopyConvertFromOptional { CopyConvertFromOptional(const NoConvertToOptional&); CopyConvertFromOptional(const absl::optional&); CopyConvertFromOptional& operator=(const CopyConvertFromOptional&) = delete; CopyConvertFromOptional& operator=(const NoConvertToOptional&); CopyConvertFromOptional& operator=( const absl::optional&); }; struct MoveConvert { MoveConvert(NoConvertToOptional&&); MoveConvert& operator=(const MoveConvert&) = delete; MoveConvert& operator=(NoConvertToOptional&&); }; struct MoveConvertFromOptional { MoveConvertFromOptional(NoConvertToOptional&&); MoveConvertFromOptional(absl::optional&&); MoveConvertFromOptional& operator=(const MoveConvertFromOptional&) = delete; MoveConvertFromOptional& operator=(NoConvertToOptional&&); MoveConvertFromOptional& operator=(absl::optional&&); }; // template absl::optional& operator=(U&& v); TEST(optionalTest, ValueAssignment) { absl::optional opt; EXPECT_FALSE(opt); opt = 42; EXPECT_TRUE(opt); EXPECT_EQ(42, opt.value()); opt = absl::nullopt; EXPECT_FALSE(opt); opt = 42; EXPECT_TRUE(opt); EXPECT_EQ(42, opt.value()); opt = 43; EXPECT_TRUE(opt); EXPECT_EQ(43, opt.value()); opt = {}; // this should clear optional EXPECT_FALSE(opt); opt = {44}; EXPECT_TRUE(opt); EXPECT_EQ(44, opt.value()); // U = const NoConvertToOptional& EXPECT_TRUE((std::is_assignable&, const NoConvertToOptional&>::value)); // U = const absl::optional& EXPECT_TRUE((std::is_assignable&, const NoConvertToOptional&>::value)); // U = const NoConvertToOptional& triggers SFINAE because // std::is_constructible_v is false EXPECT_FALSE((std::is_assignable&, const NoConvertToOptional&>::value)); // U = NoConvertToOptional EXPECT_TRUE((std::is_assignable&, NoConvertToOptional&&>::value)); // U = const NoConvertToOptional& triggers SFINAE because // std::is_constructible_v is false EXPECT_FALSE((std::is_assignable&, const NoConvertToOptional&>::value)); // U = NoConvertToOptional EXPECT_TRUE((std::is_assignable&, NoConvertToOptional&&>::value)); // U = const absl::optional& EXPECT_TRUE( (std::is_assignable&, const absl::optional&>::value)); // U = absl::optional EXPECT_TRUE( (std::is_assignable&, absl::optional&&>::value)); } // template absl::optional& operator=(const absl::optional& // rhs); template absl::optional& operator=(absl::optional&& // rhs); TEST(optionalTest, ConvertingAssignment) { absl::optional opt_i; absl::optional opt_c('c'); opt_i = opt_c; EXPECT_TRUE(opt_i); EXPECT_EQ(*opt_c, *opt_i); opt_i = absl::optional(); EXPECT_FALSE(opt_i); opt_i = absl::optional('d'); EXPECT_TRUE(opt_i); EXPECT_EQ('d', *opt_i); absl::optional opt_str; absl::optional opt_cstr("abc"); opt_str = opt_cstr; EXPECT_TRUE(opt_str); EXPECT_EQ(std::string("abc"), *opt_str); opt_str = absl::optional(); EXPECT_FALSE(opt_str); opt_str = absl::optional("def"); EXPECT_TRUE(opt_str); EXPECT_EQ(std::string("def"), *opt_str); // operator=(const absl::optional&) with U = NoConvertToOptional EXPECT_TRUE( (std::is_assignable, const absl::optional&>::value)); // operator=(const absl::optional&) with U = NoConvertToOptional // triggers SFINAE because // std::is_constructible_v is false EXPECT_FALSE( (std::is_assignable&, const absl::optional&>::value)); // operator=(absl::optional&&) with U = NoConvertToOptional EXPECT_TRUE( (std::is_assignable&, absl::optional&&>::value)); // operator=(const absl::optional&) with U = NoConvertToOptional triggers // SFINAE because std::is_constructible_v is false. operator=(U&&) with U = const // absl::optional& triggers SFINAE because // std::is_constructible&&> is true. EXPECT_FALSE( (std::is_assignable&, const absl::optional&>::value)); } TEST(optionalTest, ResetAndHasValue) { StructorListener listener; Listenable::listener = &listener; absl::optional opt; EXPECT_FALSE(opt); EXPECT_FALSE(opt.has_value()); opt.emplace(); EXPECT_TRUE(opt); EXPECT_TRUE(opt.has_value()); opt.reset(); EXPECT_FALSE(opt); EXPECT_FALSE(opt.has_value()); EXPECT_EQ(1, listener.destruct); opt.reset(); EXPECT_FALSE(opt); EXPECT_FALSE(opt.has_value()); constexpr absl::optional empty; static_assert(!empty.has_value(), ""); constexpr absl::optional nonempty(1); static_assert(nonempty.has_value(), ""); } TEST(optionalTest, Emplace) { StructorListener listener; Listenable::listener = &listener; absl::optional opt; EXPECT_FALSE(opt); opt.emplace(1); EXPECT_TRUE(opt); opt.emplace(1, 2); EXPECT_EQ(1, listener.construct1); EXPECT_EQ(1, listener.construct2); EXPECT_EQ(1, listener.destruct); absl::optional o; EXPECT_TRUE((std::is_same::value)); std::string& ref = o.emplace("abc"); EXPECT_EQ(&ref, &o.value()); } TEST(optionalTest, ListEmplace) { StructorListener listener; Listenable::listener = &listener; absl::optional opt; EXPECT_FALSE(opt); opt.emplace({1}); EXPECT_TRUE(opt); opt.emplace({1, 2}); EXPECT_EQ(2, listener.listinit); EXPECT_EQ(1, listener.destruct); absl::optional o; EXPECT_TRUE((std::is_same::value)); Listenable& ref = o.emplace({1}); EXPECT_EQ(&ref, &o.value()); } TEST(optionalTest, Swap) { absl::optional opt_empty, opt1 = 1, opt2 = 2; EXPECT_FALSE(opt_empty); EXPECT_TRUE(opt1); EXPECT_EQ(1, opt1.value()); EXPECT_TRUE(opt2); EXPECT_EQ(2, opt2.value()); swap(opt_empty, opt1); EXPECT_FALSE(opt1); EXPECT_TRUE(opt_empty); EXPECT_EQ(1, opt_empty.value()); EXPECT_TRUE(opt2); EXPECT_EQ(2, opt2.value()); swap(opt_empty, opt1); EXPECT_FALSE(opt_empty); EXPECT_TRUE(opt1); EXPECT_EQ(1, opt1.value()); EXPECT_TRUE(opt2); EXPECT_EQ(2, opt2.value()); swap(opt1, opt2); EXPECT_FALSE(opt_empty); EXPECT_TRUE(opt1); EXPECT_EQ(2, opt1.value()); EXPECT_TRUE(opt2); EXPECT_EQ(1, opt2.value()); EXPECT_TRUE(noexcept(opt1.swap(opt2))); EXPECT_TRUE(noexcept(swap(opt1, opt2))); } template struct DeletedOpAddr { int value = v; constexpr DeletedOpAddr() = default; constexpr const DeletedOpAddr* operator&() const = delete; // NOLINT DeletedOpAddr* operator&() = delete; // NOLINT }; // The static_assert featuring a constexpr call to operator->() is commented out // to document the fact that the current implementation of absl::optional // expects such usecases to be malformed and not compile. TEST(optionalTest, OperatorAddr) { constexpr int v = -1; { // constexpr constexpr absl::optional> opt(absl::in_place_t{}); static_assert(opt.has_value(), ""); // static_assert(opt->value == v, ""); static_assert((*opt).value == v, ""); } { // non-constexpr const absl::optional> opt(absl::in_place_t{}); EXPECT_TRUE(opt.has_value()); EXPECT_TRUE(opt->value == v); EXPECT_TRUE((*opt).value == v); } } TEST(optionalTest, PointerStuff) { absl::optional opt(absl::in_place, "foo"); EXPECT_EQ("foo", *opt); const auto& opt_const = opt; EXPECT_EQ("foo", *opt_const); EXPECT_EQ(opt->size(), 3u); EXPECT_EQ(opt_const->size(), 3u); constexpr absl::optional opt1(1); static_assert((*opt1).x == ConstexprType::kCtorInt, ""); } // gcc has a bug pre 4.9.1 where it doesn't do correct overload resolution // when overloads are const-qualified and *this is an raluve. // Skip that test to make the build green again when using the old compiler. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59296 is fixed in 4.9.1. #if defined(__GNUC__) && !defined(__clang__) #define GCC_VERSION \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #if GCC_VERSION < 40901 #define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG #endif #endif // MSVC has a bug with "cv-qualifiers in class construction", fixed in 2017. See // https://docs.microsoft.com/en-us/cpp/cpp-conformance-improvements-2017#bug-fixes // The compiler some incorrectly ignores the cv-qualifier when generating a // class object via a constructor call. For example: // // class optional { // constexpr T&& value() &&; // constexpr const T&& value() const &&; // } // // using COI = const absl::optional; // static_assert(2 == COI(2).value(), ""); // const && // // This should invoke the "const &&" overload but since it ignores the const // qualifier it finds the "&&" overload the best candidate. #if defined(_MSC_VER) && _MSC_VER < 1910 #define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG #endif TEST(optionalTest, Value) { using O = absl::optional; using CO = const absl::optional; using OC = absl::optional; O lvalue(absl::in_place, "lvalue"); CO clvalue(absl::in_place, "clvalue"); OC lvalue_c(absl::in_place, "lvalue_c"); EXPECT_EQ("lvalue", lvalue.value()); EXPECT_EQ("clvalue", clvalue.value()); EXPECT_EQ("lvalue_c", lvalue_c.value()); EXPECT_EQ("xvalue", O(absl::in_place, "xvalue").value()); EXPECT_EQ("xvalue_c", OC(absl::in_place, "xvalue_c").value()); #ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG EXPECT_EQ("cxvalue", CO(absl::in_place, "cxvalue").value()); #endif EXPECT_EQ("&", TypeQuals(lvalue.value())); EXPECT_EQ("c&", TypeQuals(clvalue.value())); EXPECT_EQ("c&", TypeQuals(lvalue_c.value())); EXPECT_EQ("&&", TypeQuals(O(absl::in_place, "xvalue").value())); #if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) EXPECT_EQ("c&&", TypeQuals(CO(absl::in_place, "cxvalue").value())); #endif EXPECT_EQ("c&&", TypeQuals(OC(absl::in_place, "xvalue_c").value())); #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test on volatile type using OV = absl::optional; OV lvalue_v(absl::in_place, 42); EXPECT_EQ(42, lvalue_v.value()); EXPECT_EQ(42, OV(42).value()); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); #endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test exception throw on value() absl::optional empty; #ifdef ABSL_HAVE_EXCEPTIONS EXPECT_THROW((void)empty.value(), absl::bad_optional_access); #else EXPECT_DEATH_IF_SUPPORTED((void)empty.value(), "Bad optional access"); #endif // test constexpr value() constexpr absl::optional o1(1); static_assert(1 == o1.value(), ""); // const & #if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional; static_assert(2 == COI(2).value(), ""); // const && #endif } TEST(optionalTest, DerefOperator) { using O = absl::optional; using CO = const absl::optional; using OC = absl::optional; O lvalue(absl::in_place, "lvalue"); CO clvalue(absl::in_place, "clvalue"); OC lvalue_c(absl::in_place, "lvalue_c"); EXPECT_EQ("lvalue", *lvalue); EXPECT_EQ("clvalue", *clvalue); EXPECT_EQ("lvalue_c", *lvalue_c); EXPECT_EQ("xvalue", *O(absl::in_place, "xvalue")); EXPECT_EQ("xvalue_c", *OC(absl::in_place, "xvalue_c")); #ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG EXPECT_EQ("cxvalue", *CO(absl::in_place, "cxvalue")); #endif EXPECT_EQ("&", TypeQuals(*lvalue)); EXPECT_EQ("c&", TypeQuals(*clvalue)); EXPECT_EQ("&&", TypeQuals(*O(absl::in_place, "xvalue"))); #if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) EXPECT_EQ("c&&", TypeQuals(*CO(absl::in_place, "cxvalue"))); #endif EXPECT_EQ("c&&", TypeQuals(*OC(absl::in_place, "xvalue_c"))); #if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test on volatile type using OV = absl::optional; OV lvalue_v(absl::in_place, 42); EXPECT_EQ(42, *lvalue_v); EXPECT_EQ(42, *OV(42)); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); #endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) constexpr absl::optional opt1(1); static_assert(*opt1 == 1, ""); #if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional; static_assert(*COI(2) == 2, ""); #endif } TEST(optionalTest, ValueOr) { absl::optional opt_empty, opt_set = 1.2; EXPECT_EQ(42.0, opt_empty.value_or(42)); EXPECT_EQ(1.2, opt_set.value_or(42)); EXPECT_EQ(42.0, absl::optional().value_or(42)); EXPECT_EQ(1.2, absl::optional(1.2).value_or(42)); constexpr absl::optional copt_empty, copt_set = {1.2}; static_assert(42.0 == copt_empty.value_or(42), ""); static_assert(1.2 == copt_set.value_or(42), ""); #ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG using COD = const absl::optional; static_assert(42.0 == COD().value_or(42), ""); static_assert(1.2 == COD(1.2).value_or(42), ""); #endif } // make_optional cannot be constexpr until C++17 TEST(optionalTest, make_optional) { auto opt_int = absl::make_optional(42); EXPECT_TRUE((std::is_same>::value)); EXPECT_EQ(42, opt_int); StructorListener listener; Listenable::listener = &listener; absl::optional opt0 = absl::make_optional(); EXPECT_EQ(1, listener.construct0); absl::optional opt1 = absl::make_optional(1); EXPECT_EQ(1, listener.construct1); absl::optional opt2 = absl::make_optional(1, 2); EXPECT_EQ(1, listener.construct2); absl::optional opt3 = absl::make_optional({1}); absl::optional opt4 = absl::make_optional({1, 2}); EXPECT_EQ(2, listener.listinit); // Constexpr tests on trivially copyable types // optional has trivial copy/move ctors when T is trivially copyable. // For nontrivial types with constexpr constructors, we need copy elision in // C++17 for make_optional to be constexpr. { constexpr absl::optional c_opt = absl::make_optional(42); static_assert(c_opt.value() == 42, ""); } { struct TrivialCopyable { constexpr TrivialCopyable() : x(0) {} constexpr explicit TrivialCopyable(int i) : x(i) {} int x; }; constexpr TrivialCopyable v; constexpr absl::optional c_opt0 = absl::make_optional(v); static_assert((*c_opt0).x == 0, ""); constexpr absl::optional c_opt1 = absl::make_optional(); static_assert((*c_opt1).x == 0, ""); constexpr absl::optional c_opt2 = absl::make_optional(42); static_assert((*c_opt2).x == 42, ""); } } template void optionalTest_Comparisons_EXPECT_LESS(T x, U y) { EXPECT_FALSE(x == y); EXPECT_TRUE(x != y); EXPECT_TRUE(x < y); EXPECT_FALSE(x > y); EXPECT_TRUE(x <= y); EXPECT_FALSE(x >= y); } template void optionalTest_Comparisons_EXPECT_SAME(T x, U y) { EXPECT_TRUE(x == y); EXPECT_FALSE(x != y); EXPECT_FALSE(x < y); EXPECT_FALSE(x > y); EXPECT_TRUE(x <= y); EXPECT_TRUE(x >= y); } template void optionalTest_Comparisons_EXPECT_GREATER(T x, U y) { EXPECT_FALSE(x == y); EXPECT_TRUE(x != y); EXPECT_FALSE(x < y); EXPECT_TRUE(x > y); EXPECT_FALSE(x <= y); EXPECT_TRUE(x >= y); } template void TestComparisons() { absl::optional ae, a2{2}, a4{4}; absl::optional be, b2{2}, b4{4}; V v3 = 3; // LHS: absl::nullopt, ae, a2, v3, a4 // RHS: absl::nullopt, be, b2, v3, b4 // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,absl::nullopt); optionalTest_Comparisons_EXPECT_SAME(absl::nullopt, be); optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b2); // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,v3); optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b4); optionalTest_Comparisons_EXPECT_SAME(ae, absl::nullopt); optionalTest_Comparisons_EXPECT_SAME(ae, be); optionalTest_Comparisons_EXPECT_LESS(ae, b2); optionalTest_Comparisons_EXPECT_LESS(ae, v3); optionalTest_Comparisons_EXPECT_LESS(ae, b4); optionalTest_Comparisons_EXPECT_GREATER(a2, absl::nullopt); optionalTest_Comparisons_EXPECT_GREATER(a2, be); optionalTest_Comparisons_EXPECT_SAME(a2, b2); optionalTest_Comparisons_EXPECT_LESS(a2, v3); optionalTest_Comparisons_EXPECT_LESS(a2, b4); // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(v3,absl::nullopt); optionalTest_Comparisons_EXPECT_GREATER(v3, be); optionalTest_Comparisons_EXPECT_GREATER(v3, b2); optionalTest_Comparisons_EXPECT_SAME(v3, v3); optionalTest_Comparisons_EXPECT_LESS(v3, b4); optionalTest_Comparisons_EXPECT_GREATER(a4, absl::nullopt); optionalTest_Comparisons_EXPECT_GREATER(a4, be); optionalTest_Comparisons_EXPECT_GREATER(a4, b2); optionalTest_Comparisons_EXPECT_GREATER(a4, v3); optionalTest_Comparisons_EXPECT_SAME(a4, b4); } struct Int1 { Int1() = default; Int1(int i) : i(i) {} // NOLINT(runtime/explicit) int i; }; struct Int2 { Int2() = default; Int2(int i) : i(i) {} // NOLINT(runtime/explicit) int i; }; // comparison between Int1 and Int2 constexpr bool operator==(const Int1& lhs, const Int2& rhs) { return lhs.i == rhs.i; } constexpr bool operator!=(const Int1& lhs, const Int2& rhs) { return !(lhs == rhs); } constexpr bool operator<(const Int1& lhs, const Int2& rhs) { return lhs.i < rhs.i; } constexpr bool operator<=(const Int1& lhs, const Int2& rhs) { return lhs < rhs || lhs == rhs; } constexpr bool operator>(const Int1& lhs, const Int2& rhs) { return !(lhs <= rhs); } constexpr bool operator>=(const Int1& lhs, const Int2& rhs) { return !(lhs < rhs); } TEST(optionalTest, Comparisons) { TestComparisons(); TestComparisons(); TestComparisons(); TestComparisons(); TestComparisons(); // compare absl::optional with const char* absl::optional opt_str = "abc"; const char* cstr = "abc"; EXPECT_TRUE(opt_str == cstr); // compare absl::optional with absl::optional absl::optional opt_cstr = cstr; EXPECT_TRUE(opt_str == opt_cstr); // compare absl::optional with absl::optional absl::optional e1; absl::optional e2; EXPECT_TRUE(e1 == e2); } TEST(optionalTest, SwapRegression) { StructorListener listener; Listenable::listener = &listener; { absl::optional a; absl::optional b(absl::in_place); a.swap(b); } EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.move); EXPECT_EQ(2, listener.destruct); { absl::optional a(absl::in_place); absl::optional b; a.swap(b); } EXPECT_EQ(2, listener.construct0); EXPECT_EQ(2, listener.move); EXPECT_EQ(4, listener.destruct); } TEST(optionalTest, BigStringLeakCheck) { constexpr size_t n = 1 << 16; using OS = absl::optional; OS a; OS b = absl::nullopt; OS c = std::string(n, 'c'); std::string sd(n, 'd'); OS d = sd; OS e(absl::in_place, n, 'e'); OS f; f.emplace(n, 'f'); OS ca(a); OS cb(b); OS cc(c); OS cd(d); OS ce(e); OS oa; OS ob = absl::nullopt; OS oc = std::string(n, 'c'); std::string sod(n, 'd'); OS od = sod; OS oe(absl::in_place, n, 'e'); OS of; of.emplace(n, 'f'); OS ma(std::move(oa)); OS mb(std::move(ob)); OS mc(std::move(oc)); OS md(std::move(od)); OS me(std::move(oe)); OS mf(std::move(of)); OS aa1; OS ab1 = absl::nullopt; OS ac1 = std::string(n, 'c'); std::string sad1(n, 'd'); OS ad1 = sad1; OS ae1(absl::in_place, n, 'e'); OS af1; af1.emplace(n, 'f'); OS aa2; OS ab2 = absl::nullopt; OS ac2 = std::string(n, 'c'); std::string sad2(n, 'd'); OS ad2 = sad2; OS ae2(absl::in_place, n, 'e'); OS af2; af2.emplace(n, 'f'); aa1 = af2; ab1 = ae2; ac1 = ad2; ad1 = ac2; ae1 = ab2; af1 = aa2; OS aa3; OS ab3 = absl::nullopt; OS ac3 = std::string(n, 'c'); std::string sad3(n, 'd'); OS ad3 = sad3; OS ae3(absl::in_place, n, 'e'); OS af3; af3.emplace(n, 'f'); aa3 = absl::nullopt; ab3 = absl::nullopt; ac3 = absl::nullopt; ad3 = absl::nullopt; ae3 = absl::nullopt; af3 = absl::nullopt; OS aa4; OS ab4 = absl::nullopt; OS ac4 = std::string(n, 'c'); std::string sad4(n, 'd'); OS ad4 = sad4; OS ae4(absl::in_place, n, 'e'); OS af4; af4.emplace(n, 'f'); aa4 = OS(absl::in_place, n, 'a'); ab4 = OS(absl::in_place, n, 'b'); ac4 = OS(absl::in_place, n, 'c'); ad4 = OS(absl::in_place, n, 'd'); ae4 = OS(absl::in_place, n, 'e'); af4 = OS(absl::in_place, n, 'f'); OS aa5; OS ab5 = absl::nullopt; OS ac5 = std::string(n, 'c'); std::string sad5(n, 'd'); OS ad5 = sad5; OS ae5(absl::in_place, n, 'e'); OS af5; af5.emplace(n, 'f'); std::string saa5(n, 'a'); std::string sab5(n, 'a'); std::string sac5(n, 'a'); std::string sad52(n, 'a'); std::string sae5(n, 'a'); std::string saf5(n, 'a'); aa5 = saa5; ab5 = sab5; ac5 = sac5; ad5 = sad52; ae5 = sae5; af5 = saf5; OS aa6; OS ab6 = absl::nullopt; OS ac6 = std::string(n, 'c'); std::string sad6(n, 'd'); OS ad6 = sad6; OS ae6(absl::in_place, n, 'e'); OS af6; af6.emplace(n, 'f'); aa6 = std::string(n, 'a'); ab6 = std::string(n, 'b'); ac6 = std::string(n, 'c'); ad6 = std::string(n, 'd'); ae6 = std::string(n, 'e'); af6 = std::string(n, 'f'); OS aa7; OS ab7 = absl::nullopt; OS ac7 = std::string(n, 'c'); std::string sad7(n, 'd'); OS ad7 = sad7; OS ae7(absl::in_place, n, 'e'); OS af7; af7.emplace(n, 'f'); aa7.emplace(n, 'A'); ab7.emplace(n, 'B'); ac7.emplace(n, 'C'); ad7.emplace(n, 'D'); ae7.emplace(n, 'E'); af7.emplace(n, 'F'); } TEST(optionalTest, MoveAssignRegression) { StructorListener listener; Listenable::listener = &listener; { absl::optional a; Listenable b; a = std::move(b); } EXPECT_EQ(1, listener.construct0); EXPECT_EQ(1, listener.move); EXPECT_EQ(2, listener.destruct); } TEST(optionalTest, ValueType) { EXPECT_TRUE((std::is_same::value_type, int>::value)); EXPECT_TRUE((std::is_same::value_type, std::string>::value)); EXPECT_FALSE( (std::is_same::value_type, absl::nullopt_t>::value)); } template struct is_hash_enabled_for { template ()(std::declval()))> static std::true_type test(int); template static std::false_type test(...); static constexpr bool value = decltype(test(0))::value; }; TEST(optionalTest, Hash) { std::hash> hash; std::set hashcodes; hashcodes.insert(hash(absl::nullopt)); for (int i = 0; i < 100; ++i) { hashcodes.insert(hash(i)); } EXPECT_GT(hashcodes.size(), 90u); static_assert(is_hash_enabled_for>::value, ""); static_assert(is_hash_enabled_for>::value, ""); static_assert( absl::type_traits_internal::IsHashable>::value, ""); static_assert( absl::type_traits_internal::IsHashable>::value, ""); absl::type_traits_internal::AssertHashEnabled>(); absl::type_traits_internal::AssertHashEnabled>(); #if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ static_assert(!is_hash_enabled_for>::value, ""); static_assert(!absl::type_traits_internal::IsHashable< absl::optional>::value, ""); #endif // libstdc++ std::optional is missing remove_const_t, i.e. it's using // std::hash rather than std::hash>. // Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82262 #ifndef __GLIBCXX__ static_assert(is_hash_enabled_for>::value, ""); static_assert(is_hash_enabled_for>::value, ""); std::hash> c_hash; for (int i = 0; i < 100; ++i) { EXPECT_EQ(hash(i), c_hash(i)); } #endif } struct MoveMeNoThrow { MoveMeNoThrow() : x(0) {} [[noreturn]] MoveMeNoThrow(const MoveMeNoThrow& other) : x(other.x) { LOG(FATAL) << "Should not be called."; } MoveMeNoThrow(MoveMeNoThrow&& other) noexcept : x(other.x) {} int x; }; struct MoveMeThrow { MoveMeThrow() : x(0) {} MoveMeThrow(const MoveMeThrow& other) : x(other.x) {} MoveMeThrow(MoveMeThrow&& other) : x(other.x) {} int x; }; TEST(optionalTest, NoExcept) { static_assert( std::is_nothrow_move_constructible>::value, ""); static_assert(absl::default_allocator_is_nothrow::value == std::is_nothrow_move_constructible< absl::optional>::value, ""); std::vector> v; for (int i = 0; i < 10; ++i) v.emplace_back(); } struct AnyLike { AnyLike(AnyLike&&) = default; AnyLike(const AnyLike&) = default; template ::type, typename std::enable_if< !absl::disjunction< std::is_same, absl::negation>>::value, int>::type = 0> AnyLike(ValueType&&) {} // NOLINT(runtime/explicit) AnyLike& operator=(AnyLike&&) = default; AnyLike& operator=(const AnyLike&) = default; template ::type> typename std::enable_if< absl::conjunction>, std::is_copy_constructible>::value, AnyLike&>::type operator=(ValueType&& /* rhs */) { return *this; } }; TEST(optionalTest, ConstructionConstraints) { EXPECT_TRUE((std::is_constructible>::value)); EXPECT_TRUE( (std::is_constructible&>::value)); EXPECT_TRUE((std::is_constructible, AnyLike>::value)); EXPECT_TRUE( (std::is_constructible, const AnyLike&>::value)); EXPECT_TRUE((std::is_convertible, AnyLike>::value)); EXPECT_TRUE( (std::is_convertible&, AnyLike>::value)); EXPECT_TRUE((std::is_convertible>::value)); EXPECT_TRUE( (std::is_convertible>::value)); EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE(std::is_copy_constructible>::value); } TEST(optionalTest, AssignmentConstraints) { EXPECT_TRUE((std::is_assignable>::value)); EXPECT_TRUE( (std::is_assignable&>::value)); EXPECT_TRUE((std::is_assignable&, AnyLike>::value)); EXPECT_TRUE( (std::is_assignable&, const AnyLike&>::value)); EXPECT_TRUE(std::is_move_assignable>::value); EXPECT_TRUE(absl::is_copy_assignable>::value); } #if !defined(__EMSCRIPTEN__) struct NestedClassBug { struct Inner { bool dummy = false; }; absl::optional value; }; TEST(optionalTest, InPlaceTSFINAEBug) { NestedClassBug b; ((void)b); using Inner = NestedClassBug::Inner; EXPECT_TRUE((std::is_default_constructible::value)); EXPECT_TRUE((std::is_constructible::value)); EXPECT_TRUE( (std::is_constructible, absl::in_place_t>::value)); absl::optional o(absl::in_place); EXPECT_TRUE(o.has_value()); o.emplace(); EXPECT_TRUE(o.has_value()); } #endif // !defined(__EMSCRIPTEN__) } // namespace #endif // #if !defined(ABSL_USES_STD_OPTIONAL)