// 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. // Unit tests for the variant template. The 'is' and 'IsEmpty' methods // of variant are not explicitly tested because they are used repeatedly // in building other tests. All other public variant methods should have // explicit tests. #include "absl/types/variant.h" // This test is a no-op when absl::variant is an alias for std::variant. #if !defined(ABSL_USES_STD_VARIANT) #include #include #include #include #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/port.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #ifdef ABSL_HAVE_EXCEPTIONS #define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \ EXPECT_THROW(expr, exception_t) #else #define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \ EXPECT_DEATH_IF_SUPPORTED(expr, text) #endif // ABSL_HAVE_EXCEPTIONS #define ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(...) \ ABSL_VARIANT_TEST_EXPECT_FAIL((void)(__VA_ARGS__), absl::bad_variant_access, \ "Bad variant access") struct Hashable {}; namespace std { template <> struct hash { size_t operator()(const Hashable&); }; } // namespace std struct NonHashable {}; namespace absl { ABSL_NAMESPACE_BEGIN namespace { using ::testing::DoubleEq; using ::testing::Pointee; using ::testing::VariantWith; struct MoveCanThrow { MoveCanThrow() : v(0) {} MoveCanThrow(int v) : v(v) {} // NOLINT(runtime/explicit) MoveCanThrow(const MoveCanThrow& other) : v(other.v) {} MoveCanThrow& operator=(const MoveCanThrow& /*other*/) { return *this; } int v; }; bool operator==(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v == rhs.v; } bool operator!=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v != rhs.v; } bool operator<(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v < rhs.v; } bool operator<=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v <= rhs.v; } bool operator>=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v >= rhs.v; } bool operator>(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v > rhs.v; } // This helper class allows us to determine if it was swapped with std::swap() // or with its friend swap() function. struct SpecialSwap { explicit SpecialSwap(int i) : i(i) {} friend void swap(SpecialSwap& a, SpecialSwap& b) { a.special_swap = b.special_swap = true; std::swap(a.i, b.i); } bool operator==(SpecialSwap other) const { return i == other.i; } int i; bool special_swap = false; }; struct MoveOnlyWithListConstructor { MoveOnlyWithListConstructor() = default; explicit MoveOnlyWithListConstructor(std::initializer_list /*ilist*/, int value) : value(value) {} MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default; MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) = default; int value = 0; }; #ifdef ABSL_HAVE_EXCEPTIONS struct ConversionException {}; template struct ExceptionOnConversion { operator T() const { // NOLINT(runtime/explicit) throw ConversionException(); } }; // Forces a variant into the valueless by exception state. template void ToValuelessByException(absl::variant& v) { // NOLINT try { v.template emplace<0>(ExceptionOnConversion()); } catch (ConversionException& /*e*/) { // This space intentionally left blank. } } #endif // ABSL_HAVE_EXCEPTIONS // An indexed sequence of distinct structures holding a single // value of type T template struct ValueHolder { explicit ValueHolder(const T& x) : value(x) {} typedef T value_type; value_type value; static const size_t kIndex = N; }; template const size_t ValueHolder::kIndex; // The following three functions make ValueHolder compatible with // EXPECT_EQ and EXPECT_NE template inline bool operator==(const ValueHolder& left, const ValueHolder& right) { return left.value == right.value; } template inline bool operator!=(const ValueHolder& left, const ValueHolder& right) { return left.value != right.value; } template inline std::ostream& operator<<( std::ostream& stream, const ValueHolder& object) { return stream << object.value; } // Makes a variant holding twelve uniquely typed T wrappers. template struct VariantFactory { typedef variant, ValueHolder, ValueHolder, ValueHolder> Type; }; // A typelist in 1:1 with VariantFactory, to use type driven unit tests. typedef ::testing::Types, ValueHolder, ValueHolder, ValueHolder> VariantTypes; // Increments the provided counter pointer in the destructor struct IncrementInDtor { explicit IncrementInDtor(int* counter) : counter(counter) {} ~IncrementInDtor() { *counter += 1; } int* counter; }; struct IncrementInDtorCopyCanThrow { explicit IncrementInDtorCopyCanThrow(int* counter) : counter(counter) {} IncrementInDtorCopyCanThrow(IncrementInDtorCopyCanThrow&& other) noexcept = default; IncrementInDtorCopyCanThrow(const IncrementInDtorCopyCanThrow& other) : counter(other.counter) {} IncrementInDtorCopyCanThrow& operator=( IncrementInDtorCopyCanThrow&&) noexcept = default; IncrementInDtorCopyCanThrow& operator=( IncrementInDtorCopyCanThrow const& other) { counter = other.counter; return *this; } ~IncrementInDtorCopyCanThrow() { *counter += 1; } int* counter; }; // This is defined so operator== for ValueHolder will // return true if two IncrementInDtor objects increment the same // counter inline bool operator==(const IncrementInDtor& left, const IncrementInDtor& right) { return left.counter == right.counter; } // This is defined so EXPECT_EQ can work with IncrementInDtor inline std::ostream& operator<<( std::ostream& stream, const IncrementInDtor& object) { return stream << object.counter; } // A class that can be copied, but not assigned. class CopyNoAssign { public: explicit CopyNoAssign(int value) : foo(value) {} CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} int foo; private: const CopyNoAssign& operator=(const CopyNoAssign&); }; // A class that can neither be copied nor assigned. We provide // overloads for the constructor with up to four parameters so we can // test the overloads of variant::emplace. class NonCopyable { public: NonCopyable() : value(0) {} explicit NonCopyable(int value1) : value(value1) {} NonCopyable(int value1, int value2) : value(value1 + value2) {} NonCopyable(int value1, int value2, int value3) : value(value1 + value2 + value3) {} NonCopyable(int value1, int value2, int value3, int value4) : value(value1 + value2 + value3 + value4) {} NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; int value; }; // A typed test and typed test case over the VariantTypes typelist, // from which we derive a number of tests that will execute for one of // each type. template class VariantTypesTest : public ::testing::Test {}; TYPED_TEST_SUITE(VariantTypesTest, VariantTypes); //////////////////// // [variant.ctor] // //////////////////// struct NonNoexceptDefaultConstructible { NonNoexceptDefaultConstructible() {} int value = 5; }; struct NonDefaultConstructible { NonDefaultConstructible() = delete; }; TEST(VariantTest, TestDefaultConstructor) { { using X = variant; constexpr variant x{}; ASSERT_FALSE(x.valueless_by_exception()); ASSERT_EQ(0u, x.index()); EXPECT_EQ(0, absl::get<0>(x)); EXPECT_TRUE(std::is_nothrow_default_constructible::value); } { using X = variant; X x{}; ASSERT_FALSE(x.valueless_by_exception()); ASSERT_EQ(0u, x.index()); EXPECT_EQ(5, absl::get<0>(x).value); EXPECT_FALSE(std::is_nothrow_default_constructible::value); } { using X = variant; X x{}; ASSERT_FALSE(x.valueless_by_exception()); ASSERT_EQ(0u, x.index()); EXPECT_EQ(0, absl::get<0>(x)); EXPECT_TRUE(std::is_nothrow_default_constructible::value); } { using X = variant; X x{}; ASSERT_FALSE(x.valueless_by_exception()); ASSERT_EQ(0u, x.index()); EXPECT_EQ(5, absl::get<0>(x).value); EXPECT_FALSE(std::is_nothrow_default_constructible::value); } EXPECT_FALSE( std::is_default_constructible>::value); EXPECT_FALSE((std::is_default_constructible< variant>::value)); EXPECT_TRUE((std::is_default_constructible< variant>::value)); } // Test that for each slot, copy constructing a variant with that type // produces a sensible object that correctly reports its type, and // that copies the provided value. TYPED_TEST(VariantTypesTest, TestCopyCtor) { typedef typename VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; using value_type2 = absl::variant_alternative_t<1, Variant>; using value_type3 = absl::variant_alternative_t<2, Variant>; using value_type4 = absl::variant_alternative_t<3, Variant>; const TypeParam value(TypeParam::kIndex); Variant original(value); Variant copied(original); EXPECT_TRUE(absl::holds_alternative(copied) || TypeParam::kIndex != 1); EXPECT_TRUE(absl::holds_alternative(copied) || TypeParam::kIndex != 2); EXPECT_TRUE(absl::holds_alternative(copied) || TypeParam::kIndex != 3); EXPECT_TRUE(absl::holds_alternative(copied) || TypeParam::kIndex != 4); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 1); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 2); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 3); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 4); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 1); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 2); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 3); EXPECT_TRUE((absl::get_if(&original) == absl::get_if(&copied)) || TypeParam::kIndex == 4); const TypeParam* ovalptr = absl::get_if(&original); const TypeParam* cvalptr = absl::get_if(&copied); ASSERT_TRUE(ovalptr != nullptr); ASSERT_TRUE(cvalptr != nullptr); EXPECT_EQ(*ovalptr, *cvalptr); TypeParam* mutable_ovalptr = absl::get_if(&original); TypeParam* mutable_cvalptr = absl::get_if(&copied); ASSERT_TRUE(mutable_ovalptr != nullptr); ASSERT_TRUE(mutable_cvalptr != nullptr); EXPECT_EQ(*mutable_ovalptr, *mutable_cvalptr); } template struct MoveOnly { MoveOnly() = default; explicit MoveOnly(int value) : value(value) {} MoveOnly(MoveOnly&&) = default; MoveOnly& operator=(MoveOnly&&) = default; int value = 5; }; TEST(VariantTest, TestMoveConstruct) { using V = variant, MoveOnly, MoveOnly>; V v(in_place_index<1>, 10); V v2 = std::move(v); EXPECT_EQ(10, absl::get<1>(v2).value); } // Used internally to emulate missing triviality traits for tests. template union SingleUnion { T member; }; // NOTE: These don't work with types that can't be union members. // They are just for testing. template struct is_trivially_move_constructible : std::is_move_constructible>::type {}; template struct is_trivially_move_assignable : absl::is_move_assignable>::type {}; TEST(VariantTest, NothrowMoveConstructible) { // Verify that variant is nothrow move constructible iff its template // arguments are. using U = std::unique_ptr; struct E { E(E&&) {} }; static_assert(std::is_nothrow_move_constructible>::value, ""); static_assert(std::is_nothrow_move_constructible>::value, ""); static_assert(!std::is_nothrow_move_constructible>::value, ""); } // Test that for each slot, constructing a variant with that type // produces a sensible object that correctly reports its type, and // that copies the provided value. TYPED_TEST(VariantTypesTest, TestValueCtor) { typedef typename VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; using value_type2 = absl::variant_alternative_t<1, Variant>; using value_type3 = absl::variant_alternative_t<2, Variant>; using value_type4 = absl::variant_alternative_t<3, Variant>; const TypeParam value(TypeParam::kIndex); Variant v(value); EXPECT_TRUE(absl::holds_alternative(v) || TypeParam::kIndex != 1); EXPECT_TRUE(absl::holds_alternative(v) || TypeParam::kIndex != 2); EXPECT_TRUE(absl::holds_alternative(v) || TypeParam::kIndex != 3); EXPECT_TRUE(absl::holds_alternative(v) || TypeParam::kIndex != 4); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 1); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 2); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 3); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 4); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 1); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 2); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 3); EXPECT_TRUE(nullptr != absl::get_if(&v) || TypeParam::kIndex != 4); const TypeParam* valptr = absl::get_if(&v); ASSERT_TRUE(nullptr != valptr); EXPECT_EQ(value.value, valptr->value); const TypeParam* mutable_valptr = absl::get_if(&v); ASSERT_TRUE(nullptr != mutable_valptr); EXPECT_EQ(value.value, mutable_valptr->value); } TEST(VariantTest, AmbiguousValueConstructor) { EXPECT_FALSE((std::is_convertible>::value)); EXPECT_FALSE((std::is_constructible, int>::value)); } TEST(VariantTest, InPlaceType) { using Var = variant>; Var v1(in_place_type_t(), 7); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(7, absl::get(v1)); Var v2(in_place_type_t(), "ABC"); ASSERT_TRUE(absl::holds_alternative(v2)); EXPECT_EQ("ABC", absl::get(v2)); Var v3(in_place_type_t(), "ABC", 2u); ASSERT_TRUE(absl::holds_alternative(v3)); EXPECT_EQ("AB", absl::get(v3)); Var v4(in_place_type_t{}); ASSERT_TRUE(absl::holds_alternative(v4)); Var v5(in_place_type_t>(), {1, 2, 3}); ASSERT_TRUE(absl::holds_alternative>(v5)); EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); } TEST(VariantTest, InPlaceTypeVariableTemplate) { using Var = variant>; Var v1(in_place_type, 7); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(7, absl::get(v1)); Var v2(in_place_type, "ABC"); ASSERT_TRUE(absl::holds_alternative(v2)); EXPECT_EQ("ABC", absl::get(v2)); Var v3(in_place_type, "ABC", 2u); ASSERT_TRUE(absl::holds_alternative(v3)); EXPECT_EQ("AB", absl::get(v3)); Var v4(in_place_type); ASSERT_TRUE(absl::holds_alternative(v4)); Var v5(in_place_type>, {1, 2, 3}); ASSERT_TRUE(absl::holds_alternative>(v5)); EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); } TEST(VariantTest, InPlaceTypeInitializerList) { using Var = variant; Var v1(in_place_type_t(), {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); } TEST(VariantTest, InPlaceTypeInitializerListVariabletemplate) { using Var = variant; Var v1(in_place_type, {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); } TEST(VariantTest, InPlaceIndex) { using Var = variant>; Var v1(in_place_index_t<0>(), 7); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(7, absl::get(v1)); Var v2(in_place_index_t<1>(), "ABC"); ASSERT_TRUE(absl::holds_alternative(v2)); EXPECT_EQ("ABC", absl::get(v2)); Var v3(in_place_index_t<1>(), "ABC", 2u); ASSERT_TRUE(absl::holds_alternative(v3)); EXPECT_EQ("AB", absl::get(v3)); Var v4(in_place_index_t<2>{}); EXPECT_TRUE(absl::holds_alternative(v4)); // Verify that a variant with only non-copyables can still be constructed. EXPECT_TRUE(absl::holds_alternative( variant(in_place_index_t<0>{}))); Var v5(in_place_index_t<3>(), {1, 2, 3}); ASSERT_TRUE(absl::holds_alternative>(v5)); EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); } TEST(VariantTest, InPlaceIndexVariableTemplate) { using Var = variant>; Var v1(in_place_index<0>, 7); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(7, absl::get(v1)); Var v2(in_place_index<1>, "ABC"); ASSERT_TRUE(absl::holds_alternative(v2)); EXPECT_EQ("ABC", absl::get(v2)); Var v3(in_place_index<1>, "ABC", 2u); ASSERT_TRUE(absl::holds_alternative(v3)); EXPECT_EQ("AB", absl::get(v3)); Var v4(in_place_index<2>); EXPECT_TRUE(absl::holds_alternative(v4)); // Verify that a variant with only non-copyables can still be constructed. EXPECT_TRUE(absl::holds_alternative( variant(in_place_index<0>))); Var v5(in_place_index<3>, {1, 2, 3}); ASSERT_TRUE(absl::holds_alternative>(v5)); EXPECT_THAT(absl::get>(v5), ::testing::ElementsAre(1, 2, 3)); } TEST(VariantTest, InPlaceIndexInitializerList) { using Var = variant; Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); } TEST(VariantTest, InPlaceIndexInitializerListVariableTemplate) { using Var = variant; Var v1(in_place_index<3>, {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); } //////////////////// // [variant.dtor] // //////////////////// // Make sure that the destructor destroys the contained value TEST(VariantTest, TestDtor) { typedef VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; using value_type2 = absl::variant_alternative_t<1, Variant>; using value_type3 = absl::variant_alternative_t<2, Variant>; using value_type4 = absl::variant_alternative_t<3, Variant>; int counter = 0; IncrementInDtor counter_adjuster(&counter); EXPECT_EQ(0, counter); value_type1 value1(counter_adjuster); { Variant object(value1); } EXPECT_EQ(1, counter); value_type2 value2(counter_adjuster); { Variant object(value2); } EXPECT_EQ(2, counter); value_type3 value3(counter_adjuster); { Variant object(value3); } EXPECT_EQ(3, counter); value_type4 value4(counter_adjuster); { Variant object(value4); } EXPECT_EQ(4, counter); } #ifdef ABSL_HAVE_EXCEPTIONS // See comment in absl/base/config.h #if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) TEST(VariantTest, DISABLED_TestDtorValuelessByException) #else // Test destruction when in the valueless_by_exception state. TEST(VariantTest, TestDtorValuelessByException) #endif { int counter = 0; IncrementInDtor counter_adjuster(&counter); { using Variant = VariantFactory::Type; Variant v(in_place_index<0>, counter_adjuster); EXPECT_EQ(0, counter); ToValuelessByException(v); ASSERT_TRUE(v.valueless_by_exception()); EXPECT_EQ(1, counter); } EXPECT_EQ(1, counter); } #endif // ABSL_HAVE_EXCEPTIONS ////////////////////// // [variant.assign] // ////////////////////// // Test that self-assignment doesn't destroy the current value TEST(VariantTest, TestSelfAssignment) { typedef VariantFactory::Type Variant; int counter = 0; IncrementInDtor counter_adjuster(&counter); absl::variant_alternative_t<0, Variant> value(counter_adjuster); Variant object(value); object.operator=(object); EXPECT_EQ(0, counter); // A string long enough that it's likely to defeat any inline representation // optimization. const std::string long_str(128, 'a'); std::string foo = long_str; foo = *&foo; EXPECT_EQ(long_str, foo); variant so = long_str; ASSERT_EQ(1u, so.index()); EXPECT_EQ(long_str, absl::get<1>(so)); so = *&so; ASSERT_EQ(1u, so.index()); EXPECT_EQ(long_str, absl::get<1>(so)); } // Test that assigning a variant<..., T, ...> to a variant<..., T, ...> produces // a variant<..., T, ...> with the correct value. TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValueSameTypes) { typedef typename VariantFactory::Type Variant; const TypeParam value(TypeParam::kIndex); const Variant source(value); Variant target(TypeParam(value.value + 1)); ASSERT_TRUE(absl::holds_alternative(source)); ASSERT_TRUE(absl::holds_alternative(target)); ASSERT_NE(absl::get(source), absl::get(target)); target = source; ASSERT_TRUE(absl::holds_alternative(source)); ASSERT_TRUE(absl::holds_alternative(target)); EXPECT_EQ(absl::get(source), absl::get(target)); } // Test that assisnging a variant<..., T, ...> to a variant<1, ...> // produces a variant<..., T, ...> with the correct value. TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingSourceType) { typedef typename VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; const TypeParam value(TypeParam::kIndex); const Variant source(value); ASSERT_TRUE(absl::holds_alternative(source)); Variant target(value_type1(1)); ASSERT_TRUE(absl::holds_alternative(target)); target = source; EXPECT_TRUE(absl::holds_alternative(source)); EXPECT_TRUE(absl::holds_alternative(target)); EXPECT_EQ(absl::get(source), absl::get(target)); } // Test that assigning a variant<1, ...> to a variant<..., T, ...> // produces a variant<1, ...> with the correct value. TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingTargetType) { typedef typename VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; const Variant source(value_type1(1)); ASSERT_TRUE(absl::holds_alternative(source)); const TypeParam value(TypeParam::kIndex); Variant target(value); ASSERT_TRUE(absl::holds_alternative(target)); target = source; EXPECT_TRUE(absl::holds_alternative(target)); EXPECT_TRUE(absl::holds_alternative(source)); EXPECT_EQ(absl::get(source), absl::get(target)); } // Test that operator= works, that assigning a new value destroys // the old and that assigning the new value again does not redestroy // the old TEST(VariantTest, TestAssign) { typedef VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; using value_type2 = absl::variant_alternative_t<1, Variant>; using value_type3 = absl::variant_alternative_t<2, Variant>; using value_type4 = absl::variant_alternative_t<3, Variant>; const int kSize = 4; int counter[kSize]; std::unique_ptr counter_adjustor[kSize]; for (int i = 0; i != kSize; i++) { counter[i] = 0; counter_adjustor[i] = absl::make_unique(&counter[i]); } value_type1 v1(*counter_adjustor[0]); value_type2 v2(*counter_adjustor[1]); value_type3 v3(*counter_adjustor[2]); value_type4 v4(*counter_adjustor[3]); // Test that reassignment causes destruction of old value { Variant object(v1); object = v2; object = v3; object = v4; object = v1; } EXPECT_EQ(2, counter[0]); EXPECT_EQ(1, counter[1]); EXPECT_EQ(1, counter[2]); EXPECT_EQ(1, counter[3]); std::fill(std::begin(counter), std::end(counter), 0); // Test that self-assignment does not cause destruction of old value { Variant object(v1); object.operator=(object); EXPECT_EQ(0, counter[0]); } { Variant object(v2); object.operator=(object); EXPECT_EQ(0, counter[1]); } { Variant object(v3); object.operator=(object); EXPECT_EQ(0, counter[2]); } { Variant object(v4); object.operator=(object); EXPECT_EQ(0, counter[3]); } EXPECT_EQ(1, counter[0]); EXPECT_EQ(1, counter[1]); EXPECT_EQ(1, counter[2]); EXPECT_EQ(1, counter[3]); } // This tests that we perform a backup if the copy-assign can throw but the move // cannot throw. TEST(VariantTest, TestBackupAssign) { typedef VariantFactory::Type Variant; using value_type1 = absl::variant_alternative_t<0, Variant>; using value_type2 = absl::variant_alternative_t<1, Variant>; using value_type3 = absl::variant_alternative_t<2, Variant>; using value_type4 = absl::variant_alternative_t<3, Variant>; const int kSize = 4; int counter[kSize]; std::unique_ptr counter_adjustor[kSize]; for (int i = 0; i != kSize; i++) { counter[i] = 0; counter_adjustor[i].reset(new IncrementInDtorCopyCanThrow(&counter[i])); } value_type1 v1(*counter_adjustor[0]); value_type2 v2(*counter_adjustor[1]); value_type3 v3(*counter_adjustor[2]); value_type4 v4(*counter_adjustor[3]); // Test that reassignment causes destruction of old value { Variant object(v1); object = v2; object = v3; object = v4; object = v1; } // libstdc++ doesn't pass this test #if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) EXPECT_EQ(3, counter[0]); EXPECT_EQ(2, counter[1]); EXPECT_EQ(2, counter[2]); EXPECT_EQ(2, counter[3]); #endif std::fill(std::begin(counter), std::end(counter), 0); // Test that self-assignment does not cause destruction of old value { Variant object(v1); object.operator=(object); EXPECT_EQ(0, counter[0]); } { Variant object(v2); object.operator=(object); EXPECT_EQ(0, counter[1]); } { Variant object(v3); object.operator=(object); EXPECT_EQ(0, counter[2]); } { Variant object(v4); object.operator=(object); EXPECT_EQ(0, counter[3]); } EXPECT_EQ(1, counter[0]); EXPECT_EQ(1, counter[1]); EXPECT_EQ(1, counter[2]); EXPECT_EQ(1, counter[3]); } /////////////////// // [variant.mod] // /////////////////// TEST(VariantTest, TestEmplaceBasic) { using Variant = variant; Variant v(absl::in_place_index<0>, 0); { char& emplace_result = v.emplace(); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 0); EXPECT_EQ(&emplace_result, &absl::get(v)); } // Make sure that another emplace does zero-initialization absl::get(v) = 'a'; v.emplace('b'); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 'b'); { int& emplace_result = v.emplace(); EXPECT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 0); EXPECT_EQ(&emplace_result, &absl::get(v)); } } TEST(VariantTest, TestEmplaceInitializerList) { using Var = variant; Var v1(absl::in_place_index<0>, 555); MoveOnlyWithListConstructor& emplace_result = v1.emplace({1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); EXPECT_EQ(&emplace_result, &absl::get(v1)); } TEST(VariantTest, TestEmplaceIndex) { using Variant = variant; Variant v(absl::in_place_index<0>, 555); { char& emplace_result = v.emplace<1>(); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 0); EXPECT_EQ(&emplace_result, &absl::get(v)); } // Make sure that another emplace does zero-initialization absl::get(v) = 'a'; v.emplace<1>('b'); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 'b'); { int& emplace_result = v.emplace<0>(); EXPECT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(absl::get(v), 0); EXPECT_EQ(&emplace_result, &absl::get(v)); } } TEST(VariantTest, TestEmplaceIndexInitializerList) { using Var = variant; Var v1(absl::in_place_index<0>, 555); MoveOnlyWithListConstructor& emplace_result = v1.emplace<3>({1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative(v1)); EXPECT_EQ(6, absl::get(v1).value); EXPECT_EQ(&emplace_result, &absl::get(v1)); } ////////////////////// // [variant.status] // ////////////////////// TEST(VariantTest, Index) { using Var = variant; Var v = 1; EXPECT_EQ(0u, v.index()); v = "str"; EXPECT_EQ(1u, v.index()); v = 0.; EXPECT_EQ(2u, v.index()); Var v2 = v; EXPECT_EQ(2u, v2.index()); v2.emplace(3); EXPECT_EQ(0u, v2.index()); } TEST(VariantTest, NotValuelessByException) { using Var = variant; Var v = 1; EXPECT_FALSE(v.valueless_by_exception()); v = "str"; EXPECT_FALSE(v.valueless_by_exception()); v = 0.; EXPECT_FALSE(v.valueless_by_exception()); Var v2 = v; EXPECT_FALSE(v.valueless_by_exception()); v2.emplace(3); EXPECT_FALSE(v.valueless_by_exception()); } #ifdef ABSL_HAVE_EXCEPTIONS TEST(VariantTest, IndexValuelessByException) { using Var = variant; Var v(absl::in_place_index<0>); EXPECT_EQ(0u, v.index()); ToValuelessByException(v); EXPECT_EQ(absl::variant_npos, v.index()); v = "str"; EXPECT_EQ(1u, v.index()); } TEST(VariantTest, ValuelessByException) { using Var = variant; Var v(absl::in_place_index<0>); EXPECT_FALSE(v.valueless_by_exception()); ToValuelessByException(v); EXPECT_TRUE(v.valueless_by_exception()); v = "str"; EXPECT_FALSE(v.valueless_by_exception()); } #endif // ABSL_HAVE_EXCEPTIONS //////////////////// // [variant.swap] // //////////////////// TEST(VariantTest, MemberSwap) { SpecialSwap v1(3); SpecialSwap v2(7); variant a = v1, b = v2; EXPECT_THAT(a, VariantWith(v1)); EXPECT_THAT(b, VariantWith(v2)); a.swap(b); EXPECT_THAT(a, VariantWith(v2)); EXPECT_THAT(b, VariantWith(v1)); EXPECT_TRUE(absl::get(a).special_swap); using V = variant; int i = 33; std::string s = "abc"; { // lhs and rhs holds different alternative V lhs(i), rhs(s); lhs.swap(rhs); EXPECT_THAT(lhs, VariantWith(s)); EXPECT_THAT(rhs, VariantWith(i)); } #ifdef ABSL_HAVE_EXCEPTIONS V valueless(in_place_index<0>); ToValuelessByException(valueless); { // lhs is valueless V lhs(valueless), rhs(i); lhs.swap(rhs); EXPECT_THAT(lhs, VariantWith(i)); EXPECT_TRUE(rhs.valueless_by_exception()); } { // rhs is valueless V lhs(s), rhs(valueless); lhs.swap(rhs); EXPECT_THAT(rhs, VariantWith(s)); EXPECT_TRUE(lhs.valueless_by_exception()); } { // both are valueless V lhs(valueless), rhs(valueless); lhs.swap(rhs); EXPECT_TRUE(lhs.valueless_by_exception()); EXPECT_TRUE(rhs.valueless_by_exception()); } #endif // ABSL_HAVE_EXCEPTIONS } ////////////////////// // [variant.helper] // ////////////////////// TEST(VariantTest, VariantSize) { { using Size1Variant = absl::variant; EXPECT_EQ(1u, absl::variant_size::value); EXPECT_EQ(1u, absl::variant_size::value); EXPECT_EQ(1u, absl::variant_size::value); EXPECT_EQ(1u, absl::variant_size::value); } { using Size3Variant = absl::variant; EXPECT_EQ(3u, absl::variant_size::value); EXPECT_EQ(3u, absl::variant_size::value); EXPECT_EQ(3u, absl::variant_size::value); EXPECT_EQ(3u, absl::variant_size::value); } } TEST(VariantTest, VariantAlternative) { { using V = absl::variant; EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); } { using V = absl::variant; EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE((std::is_same>::value)); EXPECT_TRUE( (std::is_same>::value)); EXPECT_TRUE(( std::is_same>::value)); } } /////////////////// // [variant.get] // /////////////////// TEST(VariantTest, HoldsAlternative) { using Var = variant; Var v = 1; EXPECT_TRUE(absl::holds_alternative(v)); EXPECT_FALSE(absl::holds_alternative(v)); EXPECT_FALSE(absl::holds_alternative(v)); v = "str"; EXPECT_FALSE(absl::holds_alternative(v)); EXPECT_TRUE(absl::holds_alternative(v)); EXPECT_FALSE(absl::holds_alternative(v)); v = 0.; EXPECT_FALSE(absl::holds_alternative(v)); EXPECT_FALSE(absl::holds_alternative(v)); EXPECT_TRUE(absl::holds_alternative(v)); Var v2 = v; EXPECT_FALSE(absl::holds_alternative(v2)); EXPECT_FALSE(absl::holds_alternative(v2)); EXPECT_TRUE(absl::holds_alternative(v2)); v2.emplace(3); EXPECT_TRUE(absl::holds_alternative(v2)); EXPECT_FALSE(absl::holds_alternative(v2)); EXPECT_FALSE(absl::holds_alternative(v2)); } TEST(VariantTest, GetIndex) { using Var = variant; { Var v(absl::in_place_index<0>, 0); using LValueGetType = decltype(absl::get<0>(v)); using RValueGetType = decltype(absl::get<0>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<0>(v), 0); EXPECT_EQ(absl::get<0>(std::move(v)), 0); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<0>(const_v)); using ConstRValueGetType = decltype(absl::get<0>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<0>(const_v), 0); EXPECT_EQ(absl::get<0>(std::move(const_v)), 0); } { Var v = std::string("Hello"); using LValueGetType = decltype(absl::get<1>(v)); using RValueGetType = decltype(absl::get<1>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<1>(v), "Hello"); EXPECT_EQ(absl::get<1>(std::move(v)), "Hello"); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<1>(const_v)); using ConstRValueGetType = decltype(absl::get<1>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<1>(const_v), "Hello"); EXPECT_EQ(absl::get<1>(std::move(const_v)), "Hello"); } { Var v = 2.0; using LValueGetType = decltype(absl::get<2>(v)); using RValueGetType = decltype(absl::get<2>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<2>(v), 2.); EXPECT_EQ(absl::get<2>(std::move(v)), 2.); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<2>(const_v)); using ConstRValueGetType = decltype(absl::get<2>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<2>(const_v), 2.); EXPECT_EQ(absl::get<2>(std::move(const_v)), 2.); } { Var v(absl::in_place_index<0>, 0); v.emplace<3>(1); using LValueGetType = decltype(absl::get<3>(v)); using RValueGetType = decltype(absl::get<3>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<3>(v), 1); EXPECT_EQ(absl::get<3>(std::move(v)), 1); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<3>(const_v)); using ConstRValueGetType = decltype(absl::get<3>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get<3>(const_v), 1); EXPECT_EQ(absl::get<3>(std::move(const_v)), 1); // NOLINT } } TEST(VariantTest, BadGetIndex) { using Var = variant; { Var v = 1; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(std::move(v))); const Var& const_v = v; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(const_v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get<1>(std::move(const_v))); // NOLINT } { Var v = std::string("Hello"); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(std::move(v))); const Var& const_v = v; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(const_v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get<0>(std::move(const_v))); // NOLINT } } TEST(VariantTest, GetType) { using Var = variant; { Var v = 1; using LValueGetType = decltype(absl::get(v)); using RValueGetType = decltype(absl::get(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(v), 1); EXPECT_EQ(absl::get(std::move(v)), 1); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get(const_v)); using ConstRValueGetType = decltype(absl::get(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(const_v), 1); EXPECT_EQ(absl::get(std::move(const_v)), 1); } { Var v = std::string("Hello"); using LValueGetType = decltype(absl::get<1>(v)); using RValueGetType = decltype(absl::get<1>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(v), "Hello"); EXPECT_EQ(absl::get(std::move(v)), "Hello"); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<1>(const_v)); using ConstRValueGetType = decltype(absl::get<1>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(const_v), "Hello"); EXPECT_EQ(absl::get(std::move(const_v)), "Hello"); } { Var v = 2.0; using LValueGetType = decltype(absl::get<2>(v)); using RValueGetType = decltype(absl::get<2>(std::move(v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(v), 2.); EXPECT_EQ(absl::get(std::move(v)), 2.); const Var& const_v = v; using ConstLValueGetType = decltype(absl::get<2>(const_v)); using ConstRValueGetType = decltype(absl::get<2>(std::move(const_v))); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(absl::get(const_v), 2.); EXPECT_EQ(absl::get(std::move(const_v)), 2.); } } TEST(VariantTest, BadGetType) { using Var = variant; { Var v = 1; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get(std::move(v))); const Var& const_v = v; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get(const_v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get(std::move(const_v))); // NOLINT } { Var v = std::string("Hello"); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(std::move(v))); const Var& const_v = v; ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get(const_v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get(std::move(const_v))); // NOLINT } } TEST(VariantTest, GetIfIndex) { using Var = variant; { Var v(absl::in_place_index<0>, 0); EXPECT_TRUE(noexcept(absl::get_if<0>(&v))); { auto* elem = absl::get_if<0>(&v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 0); { auto* bad_elem = absl::get_if<1>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } } const Var& const_v = v; EXPECT_TRUE(noexcept(absl::get_if<0>(&const_v))); { auto* elem = absl::get_if<0>(&const_v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 0); { auto* bad_elem = absl::get_if<1>(&const_v); EXPECT_TRUE( (std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&const_v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&const_v); EXPECT_EQ(bad_elem, nullptr); EXPECT_TRUE((std::is_same::value)); } } } { Var v = std::string("Hello"); EXPECT_TRUE(noexcept(absl::get_if<1>(&v))); { auto* elem = absl::get_if<1>(&v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, "Hello"); { auto* bad_elem = absl::get_if<0>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } } const Var& const_v = v; EXPECT_TRUE(noexcept(absl::get_if<1>(&const_v))); { auto* elem = absl::get_if<1>(&const_v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, "Hello"); { auto* bad_elem = absl::get_if<0>(&const_v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&const_v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&const_v); EXPECT_EQ(bad_elem, nullptr); EXPECT_TRUE((std::is_same::value)); } } } { Var v = 2.0; EXPECT_TRUE(noexcept(absl::get_if<2>(&v))); { auto* elem = absl::get_if<2>(&v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 2.0); { auto* bad_elem = absl::get_if<0>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<1>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } } const Var& const_v = v; EXPECT_TRUE(noexcept(absl::get_if<2>(&const_v))); { auto* elem = absl::get_if<2>(&const_v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 2.0); { auto* bad_elem = absl::get_if<0>(&const_v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<1>(&const_v); EXPECT_TRUE( (std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<3>(&const_v); EXPECT_EQ(bad_elem, nullptr); EXPECT_TRUE((std::is_same::value)); } } } { Var v(absl::in_place_index<0>, 0); v.emplace<3>(1); EXPECT_TRUE(noexcept(absl::get_if<3>(&v))); { auto* elem = absl::get_if<3>(&v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 1); { auto* bad_elem = absl::get_if<0>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<1>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } } const Var& const_v = v; EXPECT_TRUE(noexcept(absl::get_if<3>(&const_v))); { auto* elem = absl::get_if<3>(&const_v); EXPECT_TRUE((std::is_same::value)); ASSERT_NE(elem, nullptr); EXPECT_EQ(*elem, 1); { auto* bad_elem = absl::get_if<0>(&const_v); EXPECT_TRUE((std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<1>(&const_v); EXPECT_TRUE( (std::is_same::value)); EXPECT_EQ(bad_elem, nullptr); } { auto* bad_elem = absl::get_if<2>(&const_v); EXPECT_EQ(bad_elem, nullptr); EXPECT_TRUE((std::is_same::value)); } } } } ////////////////////// // [variant.relops] // ////////////////////// TEST(VariantTest, OperatorEquals) { variant a(1), b(1); EXPECT_TRUE(a == b); EXPECT_TRUE(b == a); EXPECT_FALSE(a != b); EXPECT_FALSE(b != a); b = "str"; EXPECT_FALSE(a == b); EXPECT_FALSE(b == a); EXPECT_TRUE(a != b); EXPECT_TRUE(b != a); b = 0; EXPECT_FALSE(a == b); EXPECT_FALSE(b == a); EXPECT_TRUE(a != b); EXPECT_TRUE(b != a); a = b = "foo"; EXPECT_TRUE(a == b); EXPECT_TRUE(b == a); EXPECT_FALSE(a != b); EXPECT_FALSE(b != a); a = "bar"; EXPECT_FALSE(a == b); EXPECT_FALSE(b == a); EXPECT_TRUE(a != b); EXPECT_TRUE(b != a); } TEST(VariantTest, OperatorRelational) { variant a(1), b(1); EXPECT_FALSE(a < b); EXPECT_FALSE(b < a); EXPECT_FALSE(a > b); EXPECT_FALSE(b > a); EXPECT_TRUE(a <= b); EXPECT_TRUE(b <= a); EXPECT_TRUE(a >= b); EXPECT_TRUE(b >= a); b = "str"; EXPECT_TRUE(a < b); EXPECT_FALSE(b < a); EXPECT_FALSE(a > b); EXPECT_TRUE(b > a); EXPECT_TRUE(a <= b); EXPECT_FALSE(b <= a); EXPECT_FALSE(a >= b); EXPECT_TRUE(b >= a); b = 0; EXPECT_FALSE(a < b); EXPECT_TRUE(b < a); EXPECT_TRUE(a > b); EXPECT_FALSE(b > a); EXPECT_FALSE(a <= b); EXPECT_TRUE(b <= a); EXPECT_TRUE(a >= b); EXPECT_FALSE(b >= a); a = b = "foo"; EXPECT_FALSE(a < b); EXPECT_FALSE(b < a); EXPECT_FALSE(a > b); EXPECT_FALSE(b > a); EXPECT_TRUE(a <= b); EXPECT_TRUE(b <= a); EXPECT_TRUE(a >= b); EXPECT_TRUE(b >= a); a = "bar"; EXPECT_TRUE(a < b); EXPECT_FALSE(b < a); EXPECT_FALSE(a > b); EXPECT_TRUE(b > a); EXPECT_TRUE(a <= b); EXPECT_FALSE(b <= a); EXPECT_FALSE(a >= b); EXPECT_TRUE(b >= a); } #ifdef ABSL_HAVE_EXCEPTIONS TEST(VariantTest, ValuelessOperatorEquals) { variant int_v(1), string_v("Hello"), valueless(absl::in_place_index<0>), other_valueless(absl::in_place_index<0>); ToValuelessByException(valueless); ToValuelessByException(other_valueless); EXPECT_TRUE(valueless == other_valueless); EXPECT_TRUE(other_valueless == valueless); EXPECT_FALSE(valueless == int_v); EXPECT_FALSE(valueless == string_v); EXPECT_FALSE(int_v == valueless); EXPECT_FALSE(string_v == valueless); EXPECT_FALSE(valueless != other_valueless); EXPECT_FALSE(other_valueless != valueless); EXPECT_TRUE(valueless != int_v); EXPECT_TRUE(valueless != string_v); EXPECT_TRUE(int_v != valueless); EXPECT_TRUE(string_v != valueless); } TEST(VariantTest, ValuelessOperatorRelational) { variant int_v(1), string_v("Hello"), valueless(absl::in_place_index<0>), other_valueless(absl::in_place_index<0>); ToValuelessByException(valueless); ToValuelessByException(other_valueless); EXPECT_FALSE(valueless < other_valueless); EXPECT_FALSE(other_valueless < valueless); EXPECT_TRUE(valueless < int_v); EXPECT_TRUE(valueless < string_v); EXPECT_FALSE(int_v < valueless); EXPECT_FALSE(string_v < valueless); EXPECT_TRUE(valueless <= other_valueless); EXPECT_TRUE(other_valueless <= valueless); EXPECT_TRUE(valueless <= int_v); EXPECT_TRUE(valueless <= string_v); EXPECT_FALSE(int_v <= valueless); EXPECT_FALSE(string_v <= valueless); EXPECT_TRUE(valueless >= other_valueless); EXPECT_TRUE(other_valueless >= valueless); EXPECT_FALSE(valueless >= int_v); EXPECT_FALSE(valueless >= string_v); EXPECT_TRUE(int_v >= valueless); EXPECT_TRUE(string_v >= valueless); EXPECT_FALSE(valueless > other_valueless); EXPECT_FALSE(other_valueless > valueless); EXPECT_FALSE(valueless > int_v); EXPECT_FALSE(valueless > string_v); EXPECT_TRUE(int_v > valueless); EXPECT_TRUE(string_v > valueless); } #endif ///////////////////// // [variant.visit] // ///////////////////// template struct ConvertTo { template T operator()(const U& u) const { return u; } }; TEST(VariantTest, VisitSimple) { variant v = "A"; std::string str = absl::visit(ConvertTo{}, v); EXPECT_EQ("A", str); v = std::string("B"); absl::string_view piece = absl::visit(ConvertTo{}, v); EXPECT_EQ("B", piece); struct StrLen { size_t operator()(const char* s) const { return strlen(s); } size_t operator()(const std::string& s) const { return s.size(); } }; v = "SomeStr"; EXPECT_EQ(7u, absl::visit(StrLen{}, v)); v = std::string("VeryLargeThisTime"); EXPECT_EQ(17u, absl::visit(StrLen{}, v)); } TEST(VariantTest, VisitRValue) { variant v = std::string("X"); struct Visitor { bool operator()(const std::string&) const { return false; } bool operator()(std::string&&) const { return true; } // NOLINT int operator()(const std::string&, const std::string&) const { return 0; } int operator()(const std::string&, std::string&&) const { return 1; } // NOLINT int operator()(std::string&&, const std::string&) const { return 2; } // NOLINT int operator()(std::string&&, std::string&&) const { return 3; } // NOLINT }; EXPECT_FALSE(absl::visit(Visitor{}, v)); EXPECT_TRUE(absl::visit(Visitor{}, std::move(v))); // Also test the variadic overload. EXPECT_EQ(0, absl::visit(Visitor{}, v, v)); EXPECT_EQ(1, absl::visit(Visitor{}, v, std::move(v))); EXPECT_EQ(2, absl::visit(Visitor{}, std::move(v), v)); EXPECT_EQ(3, absl::visit(Visitor{}, std::move(v), std::move(v))); } TEST(VariantTest, VisitRValueVisitor) { variant v = std::string("X"); struct Visitor { bool operator()(const std::string&) const& { return false; } bool operator()(const std::string&) && { return true; } }; Visitor visitor; EXPECT_FALSE(absl::visit(visitor, v)); EXPECT_TRUE(absl::visit(Visitor{}, v)); } TEST(VariantTest, VisitResultTypeDifferent) { variant v = std::string("X"); struct LValue_LValue {}; struct RValue_LValue {}; struct LValue_RValue {}; struct RValue_RValue {}; struct Visitor { LValue_LValue operator()(const std::string&) const& { return {}; } RValue_LValue operator()(std::string&&) const& { return {}; } // NOLINT LValue_RValue operator()(const std::string&) && { return {}; } RValue_RValue operator()(std::string&&) && { return {}; } // NOLINT } visitor; EXPECT_TRUE( (std::is_same::value)); EXPECT_TRUE( (std::is_same::value)); EXPECT_TRUE(( std::is_same::value)); EXPECT_TRUE( (std::is_same::value)); } TEST(VariantTest, VisitVariadic) { using A = variant; using B = variant, absl::string_view>; struct Visitor { std::pair operator()(int a, std::unique_ptr b) const { return {a, *b}; } std::pair operator()(absl::string_view a, std::unique_ptr b) const { return {static_cast(a.size()), static_cast(*b)}; } std::pair operator()(int a, absl::string_view b) const { return {a, static_cast(b.size())}; } std::pair operator()(absl::string_view a, absl::string_view b) const { return {static_cast(a.size()), static_cast(b.size())}; } }; EXPECT_THAT(absl::visit(Visitor(), A(1), B(std::unique_ptr(new int(7)))), ::testing::Pair(1, 7)); EXPECT_THAT(absl::visit(Visitor(), A(1), B(absl::string_view("ABC"))), ::testing::Pair(1, 3)); EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), B(std::unique_ptr(new int(7)))), ::testing::Pair(5, 7)); EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), B(absl::string_view("ABC"))), ::testing::Pair(5, 3)); } TEST(VariantTest, VisitNoArgs) { EXPECT_EQ(5, absl::visit([] { return 5; })); } struct ConstFunctor { int operator()(int a, int b) const { return a - b; } }; struct MutableFunctor { int operator()(int a, int b) { return a - b; } }; struct Class { int Method(int a, int b) { return a - b; } int ConstMethod(int a, int b) const { return a - b; } int member; }; TEST(VariantTest, VisitReferenceWrapper) { ConstFunctor cf; MutableFunctor mf; absl::variant three = 3; absl::variant two = 2; EXPECT_EQ(1, absl::visit(std::cref(cf), three, two)); EXPECT_EQ(1, absl::visit(std::ref(cf), three, two)); EXPECT_EQ(1, absl::visit(std::ref(mf), three, two)); } // libstdc++ std::variant doesn't support the INVOKE semantics. #if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) TEST(VariantTest, VisitMemberFunction) { absl::variant> p(absl::make_unique()); absl::variant> cp( absl::make_unique()); absl::variant three = 3; absl::variant two = 2; EXPECT_EQ(1, absl::visit(&Class::Method, p, three, two)); EXPECT_EQ(1, absl::visit(&Class::ConstMethod, p, three, two)); EXPECT_EQ(1, absl::visit(&Class::ConstMethod, cp, three, two)); } TEST(VariantTest, VisitDataMember) { absl::variant> p(absl::make_unique(Class{42})); absl::variant> cp( absl::make_unique(Class{42})); EXPECT_EQ(42, absl::visit(&Class::member, p)); absl::visit(&Class::member, p) = 5; EXPECT_EQ(5, absl::visit(&Class::member, p)); EXPECT_EQ(42, absl::visit(&Class::member, cp)); } #endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) ///////////////////////// // [variant.monostate] // ///////////////////////// TEST(VariantTest, MonostateBasic) { absl::monostate mono; (void)mono; // TODO(mattcalabrese) Expose move triviality metafunctions in absl. EXPECT_TRUE(absl::is_trivially_default_constructible::value); EXPECT_TRUE(is_trivially_move_constructible::value); EXPECT_TRUE(absl::is_trivially_copy_constructible::value); EXPECT_TRUE(is_trivially_move_assignable::value); EXPECT_TRUE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } TEST(VariantTest, VariantMonostateDefaultConstruction) { absl::variant var; EXPECT_EQ(var.index(), 0u); } //////////////////////////////// // [variant.monostate.relops] // //////////////////////////////// TEST(VariantTest, MonostateComparisons) { absl::monostate lhs, rhs; EXPECT_EQ(lhs, lhs); EXPECT_EQ(lhs, rhs); EXPECT_FALSE(lhs != lhs); EXPECT_FALSE(lhs != rhs); EXPECT_FALSE(lhs < lhs); EXPECT_FALSE(lhs < rhs); EXPECT_FALSE(lhs > lhs); EXPECT_FALSE(lhs > rhs); EXPECT_LE(lhs, lhs); EXPECT_LE(lhs, rhs); EXPECT_GE(lhs, lhs); EXPECT_GE(lhs, rhs); EXPECT_TRUE(noexcept(std::declval() == std::declval())); EXPECT_TRUE(noexcept(std::declval() != std::declval())); EXPECT_TRUE(noexcept(std::declval() < std::declval())); EXPECT_TRUE(noexcept(std::declval() > std::declval())); EXPECT_TRUE(noexcept(std::declval() <= std::declval())); EXPECT_TRUE(noexcept(std::declval() >= std::declval())); } /////////////////////// // [variant.specalg] // /////////////////////// TEST(VariantTest, NonmemberSwap) { using std::swap; SpecialSwap v1(3); SpecialSwap v2(7); variant a = v1, b = v2; EXPECT_THAT(a, VariantWith(v1)); EXPECT_THAT(b, VariantWith(v2)); std::swap(a, b); EXPECT_THAT(a, VariantWith(v2)); EXPECT_THAT(b, VariantWith(v1)); #ifndef ABSL_USES_STD_VARIANT EXPECT_FALSE(absl::get(a).special_swap); #endif swap(a, b); EXPECT_THAT(a, VariantWith(v1)); EXPECT_THAT(b, VariantWith(v2)); EXPECT_TRUE(absl::get(b).special_swap); } ////////////////////////// // [variant.bad.access] // ////////////////////////// TEST(VariantTest, BadAccess) { EXPECT_TRUE(noexcept(absl::bad_variant_access())); absl::bad_variant_access exception_obj; std::exception* base = &exception_obj; (void)base; } //////////////////// // [variant.hash] // //////////////////// TEST(VariantTest, MonostateHash) { absl::monostate mono, other_mono; std::hash const hasher{}; static_assert(std::is_same::value, ""); EXPECT_EQ(hasher(mono), hasher(other_mono)); } TEST(VariantTest, Hash) { static_assert(type_traits_internal::IsHashable>::value, ""); static_assert(type_traits_internal::IsHashable>::value, ""); static_assert(type_traits_internal::IsHashable>::value, ""); #if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ static_assert(!type_traits_internal::IsHashable>::value, ""); static_assert( !type_traits_internal::IsHashable>::value, ""); #endif // MSVC std::hash does not use the index, thus produce the same // result on the same value as different alternative. #if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { // same value as different alternative variant v0(in_place_index<0>, 42); variant v1(in_place_index<1>, 42); std::hash> hash; EXPECT_NE(hash(v0), hash(v1)); } #endif // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { std::hash> hash; std::set hashcodes; for (int i = 0; i < 100; ++i) { hashcodes.insert(hash(i)); } EXPECT_GT(hashcodes.size(), 90u); // test const-qualified static_assert(type_traits_internal::IsHashable>::value, ""); static_assert( type_traits_internal::IsHashable>::value, ""); std::hash> c_hash; for (int i = 0; i < 100; ++i) { EXPECT_EQ(hash(i), c_hash(i)); } } } //////////////////////////////////////// // Miscellaneous and deprecated tests // //////////////////////////////////////// // Test that a set requiring a basic type conversion works correctly #if !defined(ABSL_USES_STD_VARIANT) TEST(VariantTest, TestConvertingSet) { typedef variant Variant; Variant v(1.0); const int two = 2; v = two; EXPECT_TRUE(absl::holds_alternative(v)); ASSERT_TRUE(nullptr != absl::get_if(&v)); EXPECT_DOUBLE_EQ(2, absl::get(v)); } #endif // ABSL_USES_STD_VARIANT // Test that a vector of variants behaves reasonably. TEST(VariantTest, Container) { typedef variant Variant; // Creation of vector should work std::vector vec; vec.push_back(Variant(10)); vec.push_back(Variant(20.0f)); // Vector resizing should work if we supply a value for new slots vec.resize(10, Variant(0)); } // Test that a variant with a non-copyable type can be constructed and // manipulated to some degree. TEST(VariantTest, TestVariantWithNonCopyableType) { typedef variant Variant; const int kValue = 1; Variant v(kValue); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(kValue, absl::get(v)); } // Test that a variant with a non-copyable type can be transformed to // the non-copyable type with a call to `emplace` for different numbers // of arguments. We do not need to test this for each of T1 ... T8 // because `emplace` does not overload on T1 ... to T8, so if this // works for any one of T1 ... T8, then it works for all of them. We // do need to test that it works with varying numbers of parameters // though. TEST(VariantTest, TestEmplace) { typedef variant Variant; const int kValue = 1; Variant v(kValue); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(kValue, absl::get(v)); // emplace with zero arguments, then back to 'int' v.emplace(); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(0, absl::get(v).value); v = kValue; ASSERT_TRUE(absl::holds_alternative(v)); // emplace with one argument: v.emplace(1); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(1, absl::get(v).value); v = kValue; ASSERT_TRUE(absl::holds_alternative(v)); // emplace with two arguments: v.emplace(1, 2); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(3, absl::get(v).value); v = kValue; ASSERT_TRUE(absl::holds_alternative(v)); // emplace with three arguments v.emplace(1, 2, 3); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(6, absl::get(v).value); v = kValue; ASSERT_TRUE(absl::holds_alternative(v)); // emplace with four arguments v.emplace(1, 2, 3, 4); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(10, absl::get(v).value); v = kValue; ASSERT_TRUE(absl::holds_alternative(v)); } TEST(VariantTest, TestEmplaceDestroysCurrentValue) { typedef variant Variant; int counter = 0; Variant v(0); ASSERT_TRUE(absl::holds_alternative(v)); v.emplace(&counter); ASSERT_TRUE(absl::holds_alternative(v)); ASSERT_EQ(0, counter); v.emplace(); ASSERT_TRUE(absl::holds_alternative(v)); EXPECT_EQ(1, counter); } TEST(VariantTest, TestMoveSemantics) { typedef variant, std::unique_ptr> Variant; // Construct a variant by moving from an element value. Variant v(absl::WrapUnique(new int(10))); EXPECT_TRUE(absl::holds_alternative>(v)); // Construct a variant by moving from another variant. Variant v2(std::move(v)); ASSERT_TRUE(absl::holds_alternative>(v2)); ASSERT_NE(nullptr, absl::get>(v2)); EXPECT_EQ(10, *absl::get>(v2)); // Moving from a variant object leaves it holding moved-from value of the // same element type. EXPECT_TRUE(absl::holds_alternative>(v)); ASSERT_NE(nullptr, absl::get_if>(&v)); EXPECT_EQ(nullptr, absl::get>(v)); // Assign a variant from an element value by move. v = absl::make_unique("foo"); ASSERT_TRUE(absl::holds_alternative>(v)); EXPECT_EQ("foo", *absl::get>(v)); // Move-assign a variant. v2 = std::move(v); ASSERT_TRUE(absl::holds_alternative>(v2)); EXPECT_EQ("foo", *absl::get>(v2)); EXPECT_TRUE(absl::holds_alternative>(v)); } variant PassThrough(const variant& arg) { return arg; } TEST(VariantTest, TestImplicitConversion) { EXPECT_TRUE(absl::holds_alternative(PassThrough(0))); // We still need the explicit cast for std::string, because C++ won't apply // two user-defined implicit conversions in a row. EXPECT_TRUE( absl::holds_alternative(PassThrough(std::string("foo")))); } struct Convertible2; struct Convertible1 { Convertible1() {} Convertible1(const Convertible1&) {} Convertible1& operator=(const Convertible1&) { return *this; } // implicit conversion from Convertible2 Convertible1(const Convertible2&) {} // NOLINT(runtime/explicit) }; struct Convertible2 { Convertible2() {} Convertible2(const Convertible2&) {} Convertible2& operator=(const Convertible2&) { return *this; } // implicit conversion from Convertible1 Convertible2(const Convertible1&) {} // NOLINT(runtime/explicit) }; TEST(VariantTest, TestRvalueConversion) { #if !defined(ABSL_USES_STD_VARIANT) variant var( ConvertVariantTo>( variant(0))); ASSERT_TRUE(absl::holds_alternative(var)); EXPECT_EQ(0.0, absl::get(var)); var = ConvertVariantTo>( variant("foo")); ASSERT_TRUE(absl::holds_alternative(var)); EXPECT_EQ("foo", absl::get(var)); variant singleton( ConvertVariantTo>(variant(42))); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_EQ(42.0, absl::get(singleton)); singleton = ConvertVariantTo>(variant(3.14f)); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); singleton = ConvertVariantTo>(variant(0)); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_EQ(0.0, absl::get(singleton)); variant variant2( ConvertVariantTo>(variant(42))); ASSERT_TRUE(absl::holds_alternative(variant2)); EXPECT_EQ(42, absl::get(variant2)); variant2 = ConvertVariantTo>(variant(42u)); ASSERT_TRUE(absl::holds_alternative(variant2)); EXPECT_EQ(42u, absl::get(variant2)); #endif // !ABSL_USES_STD_VARIANT variant variant3( ConvertVariantTo>( (variant(Convertible1())))); ASSERT_TRUE(absl::holds_alternative(variant3)); variant3 = ConvertVariantTo>( variant(Convertible2())); ASSERT_TRUE(absl::holds_alternative(variant3)); } TEST(VariantTest, TestLvalueConversion) { #if !defined(ABSL_USES_STD_VARIANT) variant source1 = 0; variant destination( ConvertVariantTo>(source1)); ASSERT_TRUE(absl::holds_alternative(destination)); EXPECT_EQ(0.0, absl::get(destination)); variant source2 = "foo"; destination = ConvertVariantTo>(source2); ASSERT_TRUE(absl::holds_alternative(destination)); EXPECT_EQ("foo", absl::get(destination)); variant source3(42); variant singleton(ConvertVariantTo>(source3)); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_EQ(42.0, absl::get(singleton)); source3 = 3.14f; singleton = ConvertVariantTo>(source3); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); variant source4(0); singleton = ConvertVariantTo>(source4); ASSERT_TRUE(absl::holds_alternative(singleton)); EXPECT_EQ(0.0, absl::get(singleton)); variant source5(42); variant variant2( ConvertVariantTo>(source5)); ASSERT_TRUE(absl::holds_alternative(variant2)); EXPECT_EQ(42, absl::get(variant2)); variant source6(42u); variant2 = ConvertVariantTo>(source6); ASSERT_TRUE(absl::holds_alternative(variant2)); EXPECT_EQ(42u, absl::get(variant2)); #endif variant source7((Convertible1())); variant variant3( ConvertVariantTo>(source7)); ASSERT_TRUE(absl::holds_alternative(variant3)); source7 = Convertible2(); variant3 = ConvertVariantTo>(source7); ASSERT_TRUE(absl::holds_alternative(variant3)); } TEST(VariantTest, TestMoveConversion) { using Variant = variant, std::unique_ptr>; using OtherVariant = variant, std::unique_ptr>; Variant var( ConvertVariantTo(OtherVariant{absl::make_unique(0)})); ASSERT_TRUE(absl::holds_alternative>(var)); ASSERT_NE(absl::get>(var), nullptr); EXPECT_EQ(0, *absl::get>(var)); var = ConvertVariantTo( OtherVariant(absl::make_unique("foo"))); ASSERT_TRUE(absl::holds_alternative>(var)); EXPECT_EQ("foo", *absl::get>(var)); } TEST(VariantTest, DoesNotMoveFromLvalues) { // We use shared_ptr here because it's both copyable and movable, and // a moved-from shared_ptr is guaranteed to be null, so we can detect // whether moving or copying has occurred. using Variant = variant, std::shared_ptr>; using OtherVariant = variant, std::shared_ptr>; Variant v1(std::make_shared(0)); // Test copy constructor Variant v2(v1); EXPECT_EQ(absl::get>(v1), absl::get>(v2)); // Test copy-assignment operator v1 = std::make_shared("foo"); v2 = v1; EXPECT_EQ(absl::get>(v1), absl::get>(v2)); // Test converting copy constructor OtherVariant other(std::make_shared(0)); Variant v3(ConvertVariantTo(other)); EXPECT_EQ(absl::get>(other), absl::get>(v3)); other = std::make_shared("foo"); v3 = ConvertVariantTo(other); EXPECT_EQ(absl::get>(other), absl::get>(v3)); } TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { #if !defined(ABSL_USES_STD_VARIANT) variant var( ConvertVariantTo>( variant(3))); EXPECT_THAT(absl::get_if(&var), Pointee(3.0)); var = ConvertVariantTo>( variant("foo")); EXPECT_THAT(absl::get_if(&var), Pointee(std::string("foo"))); variant singleton( ConvertVariantTo>(variant(42))); EXPECT_THAT(absl::get_if(&singleton), Pointee(42.0)); singleton = ConvertVariantTo>(variant(3.14f)); EXPECT_THAT(absl::get_if(&singleton), Pointee(DoubleEq(3.14f))); singleton = ConvertVariantTo>(variant(3)); EXPECT_THAT(absl::get_if(&singleton), Pointee(3.0)); variant variant2( ConvertVariantTo>(variant(42))); EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); variant2 = ConvertVariantTo>(variant(42u)); EXPECT_THAT(absl::get_if(&variant2), Pointee(42u)); #endif variant variant3( ConvertVariantTo>( (variant(Convertible1())))); ASSERT_TRUE(absl::holds_alternative(variant3)); variant3 = ConvertVariantTo>( variant(Convertible2())); ASSERT_TRUE(absl::holds_alternative(variant3)); } TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { #if !defined(ABSL_USES_STD_VARIANT) variant source1 = 3; variant destination( ConvertVariantTo>(source1)); EXPECT_THAT(absl::get_if(&destination), Pointee(3.0)); variant source2 = "foo"; destination = ConvertVariantTo>(source2); EXPECT_THAT(absl::get_if(&destination), Pointee(std::string("foo"))); variant source3(42); variant singleton(ConvertVariantTo>(source3)); EXPECT_THAT(absl::get_if(&singleton), Pointee(42.0)); source3 = 3.14f; singleton = ConvertVariantTo>(source3); EXPECT_FLOAT_EQ(3.14f, static_cast(absl::get(singleton))); EXPECT_THAT(absl::get_if(&singleton), Pointee(DoubleEq(3.14f))); variant source4(3); singleton = ConvertVariantTo>(source4); EXPECT_THAT(absl::get_if(&singleton), Pointee(3.0)); variant source5(42); variant variant2( ConvertVariantTo>(source5)); EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); variant source6(42u); variant2 = ConvertVariantTo>(source6); EXPECT_THAT(absl::get_if(&variant2), Pointee(42u)); #endif // !ABSL_USES_STD_VARIANT variant source7((Convertible1())); variant variant3( ConvertVariantTo>(source7)); ASSERT_TRUE(absl::holds_alternative(variant3)); source7 = Convertible2(); variant3 = ConvertVariantTo>(source7); ASSERT_TRUE(absl::holds_alternative(variant3)); } TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { using Variant = variant, std::unique_ptr>; using OtherVariant = variant, std::unique_ptr>; Variant var( ConvertVariantTo(OtherVariant{absl::make_unique(3)})); EXPECT_THAT(absl::get_if>(&var), Pointee(Pointee(3))); var = ConvertVariantTo( OtherVariant(absl::make_unique("foo"))); EXPECT_THAT(absl::get_if>(&var), Pointee(Pointee(std::string("foo")))); } // If all alternatives are trivially copy/move constructible, variant should // also be trivially copy/move constructible. This is not required by the // standard and we know that libstdc++ variant doesn't have this feature. // For more details see the paper: // http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html #if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) #define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1 #endif TEST(VariantTest, TestCopyAndMoveTypeTraits) { EXPECT_TRUE(std::is_copy_constructible>::value); EXPECT_TRUE(absl::is_copy_assignable>::value); EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_TRUE(std::is_move_constructible>>::value); EXPECT_TRUE(absl::is_move_assignable>>::value); EXPECT_FALSE( std::is_copy_constructible>>::value); EXPECT_FALSE(absl::is_copy_assignable>>::value); EXPECT_FALSE( absl::is_trivially_copy_constructible>::value); EXPECT_FALSE(absl::is_trivially_copy_assignable>::value); #if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY EXPECT_TRUE(absl::is_trivially_copy_constructible>::value); EXPECT_TRUE(absl::is_trivially_copy_assignable>::value); EXPECT_TRUE(is_trivially_move_constructible>::value); EXPECT_TRUE(is_trivially_move_assignable>::value); #endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY } TEST(VariantTest, TestVectorOfMoveonlyVariant) { // Verify that variant works correctly as a std::vector element. std::vector, std::string>> vec; vec.push_back(absl::make_unique(42)); vec.emplace_back("Hello"); vec.reserve(3); auto another_vec = std::move(vec); // As a sanity check, verify vector contents. ASSERT_EQ(2u, another_vec.size()); EXPECT_EQ(42, *absl::get>(another_vec[0])); EXPECT_EQ("Hello", absl::get(another_vec[1])); } TEST(VariantTest, NestedVariant) { #if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY static_assert(absl::is_trivially_copy_constructible>(), ""); static_assert(absl::is_trivially_copy_assignable>(), ""); static_assert(is_trivially_move_constructible>(), ""); static_assert(is_trivially_move_assignable>(), ""); static_assert(absl::is_trivially_copy_constructible>>(), ""); static_assert(absl::is_trivially_copy_assignable>>(), ""); static_assert(is_trivially_move_constructible>>(), ""); static_assert(is_trivially_move_assignable>>(), ""); #endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY variant x(42); variant> y(x); variant> z(y); EXPECT_TRUE(absl::holds_alternative>(z)); EXPECT_EQ(x, absl::get>(z)); } struct TriviallyDestructible { TriviallyDestructible(TriviallyDestructible&&) {} TriviallyDestructible(const TriviallyDestructible&) {} TriviallyDestructible& operator=(TriviallyDestructible&&) { return *this; } TriviallyDestructible& operator=(const TriviallyDestructible&) { return *this; } }; struct TriviallyMovable { TriviallyMovable(TriviallyMovable&&) = default; TriviallyMovable(TriviallyMovable const&) {} TriviallyMovable& operator=(const TriviallyMovable&) { return *this; } }; struct TriviallyCopyable { TriviallyCopyable(const TriviallyCopyable&) = default; TriviallyCopyable& operator=(const TriviallyCopyable&) { return *this; } }; struct TriviallyMoveAssignable { TriviallyMoveAssignable(TriviallyMoveAssignable&&) = default; TriviallyMoveAssignable(const TriviallyMoveAssignable&) {} TriviallyMoveAssignable& operator=(TriviallyMoveAssignable&&) = default; TriviallyMoveAssignable& operator=(const TriviallyMoveAssignable&) { return *this; } }; struct TriviallyCopyAssignable {}; #if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY TEST(VariantTest, TestTriviality) { { using TrivDestVar = absl::variant; EXPECT_FALSE(is_trivially_move_constructible::value); EXPECT_FALSE(absl::is_trivially_copy_constructible::value); EXPECT_FALSE(is_trivially_move_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } { using TrivMoveVar = absl::variant; EXPECT_TRUE(is_trivially_move_constructible::value); EXPECT_FALSE(absl::is_trivially_copy_constructible::value); EXPECT_FALSE(is_trivially_move_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } { using TrivCopyVar = absl::variant; EXPECT_TRUE(is_trivially_move_constructible::value); EXPECT_TRUE(absl::is_trivially_copy_constructible::value); EXPECT_FALSE(is_trivially_move_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } { using TrivMoveAssignVar = absl::variant; EXPECT_TRUE(is_trivially_move_constructible::value); EXPECT_FALSE( absl::is_trivially_copy_constructible::value); EXPECT_TRUE(is_trivially_move_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } { using TrivCopyAssignVar = absl::variant; EXPECT_TRUE(is_trivially_move_constructible::value); EXPECT_TRUE( absl::is_trivially_copy_constructible::value); EXPECT_TRUE(is_trivially_move_assignable::value); EXPECT_TRUE(absl::is_trivially_copy_assignable::value); EXPECT_TRUE(absl::is_trivially_destructible::value); } } #endif // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY // To verify that absl::variant correctly use the nontrivial move ctor of its // member rather than use the trivial copy constructor. TEST(VariantTest, MoveCtorBug) { // To simulate std::tuple in libstdc++. struct TrivialCopyNontrivialMove { TrivialCopyNontrivialMove() = default; TrivialCopyNontrivialMove(const TrivialCopyNontrivialMove&) = default; TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) { called = true; } bool called = false; }; { using V = absl::variant; V v1(absl::in_place_index<0>); // this should invoke the move ctor, rather than the trivial copy ctor. V v2(std::move(v1)); EXPECT_TRUE(absl::get<0>(v2).called); } { // this case failed to compile before our fix due to a GCC bug. using V = absl::variant; V v1(absl::in_place_index<1>); // this should invoke the move ctor, rather than the trivial copy ctor. V v2(std::move(v1)); EXPECT_TRUE(absl::get<1>(v2).called); } } } // namespace ABSL_NAMESPACE_END } // namespace absl #endif // #if !defined(ABSL_USES_STD_VARIANT)