diff --git a/cetlvast/suites/unittest/test_pf17_optional.cpp b/cetlvast/suites/unittest/test_pf17_optional.cpp index 55a509d3..a0ab6556 100644 --- a/cetlvast/suites/unittest/test_pf17_optional.cpp +++ b/cetlvast/suites/unittest/test_pf17_optional.cpp @@ -939,3 +939,152 @@ TYPED_TEST(test_optional_combinations, assignment_2) { test_assignment_2::test(); } + +/// ------------------------------------------------------------------------------------------------ + +/// For the move assignment to work, T shall be both +/// (move-constructible or copy-constructible) and (move-assignable or copy-assignable). +/// Notes: +/// - A type does not have to implement move assignment operator in order to satisfy MoveAssignable: +/// a copy assignment operator that takes its parameter by value or as a const Type&, will bind to rvalue argument. +/// - A type does not have to implement a move constructor to satisfy MoveConstructible: +/// a copy constructor that takes a const T& argument can bind rvalue expressions. +template +struct test_assignment_3 +{ + static void test() + { + std::uint32_t destructed = 0; + optional opt1; + optional opt2; + EXPECT_FALSE(opt1); + EXPECT_FALSE(opt2); + // Empty to empty. + opt1 = std::move(opt2); + EXPECT_FALSE(opt1); + EXPECT_FALSE(opt2); + // Non-empty to empty. A copy/move ctor is invoked. + opt1.emplace().configure_destruction_counter(&destructed); + EXPECT_TRUE(opt1); + EXPECT_FALSE(opt2); + opt2 = std::move(opt1); + EXPECT_TRUE(opt1); + EXPECT_TRUE(opt2); + // Check opt1 counters. + EXPECT_EQ(0U, opt1->get_copy_ctor_count()); + EXPECT_EQ(0U, opt1->get_move_ctor_count()); + EXPECT_EQ(0U, opt1->get_copy_assignment_count()); + EXPECT_EQ(0U, opt1->get_move_assignment_count()); + // Check opt2 counters. + EXPECT_EQ(((T::copy_ctor_policy_value == policy_nontrivial) && (T::move_ctor_policy_value == policy_deleted)) + ? 1 + : 0, + opt2->get_copy_ctor_count()); + EXPECT_EQ((T::move_ctor_policy_value == policy_nontrivial) ? 1 : 0, opt2->get_move_ctor_count()); + EXPECT_EQ(0U, opt2->get_copy_assignment_count()); + EXPECT_EQ(0U, opt2->get_move_assignment_count()); + EXPECT_EQ(0U, destructed); + // Non-empty to non-empty. A copy/move assignment is invoked. + opt1 = std::move(opt2); + EXPECT_TRUE(opt1); + EXPECT_TRUE(opt2); + // Check opt1 counters. The copy/move ctor count is copied over from opt2! + EXPECT_EQ(((T::copy_ctor_policy_value == policy_nontrivial) && (T::move_ctor_policy_value == policy_deleted)) + ? 1 + : 0, + opt1->get_copy_ctor_count()); + EXPECT_EQ((T::move_ctor_policy_value == policy_nontrivial) ? 1 : 0, opt1->get_move_ctor_count()); + EXPECT_EQ(((T::copy_assignment_policy_value == policy_nontrivial) && + (T::move_assignment_policy_value == policy_deleted)) + ? 1 + : 0, + opt1->get_copy_assignment_count()); + EXPECT_EQ((T::move_assignment_policy_value == policy_nontrivial) ? 1 : 0, opt1->get_move_assignment_count()); + // Check opt2 counters. + EXPECT_EQ(((T::copy_ctor_policy_value == policy_nontrivial) && (T::move_ctor_policy_value == policy_deleted)) + ? 1 + : 0, + opt2->get_copy_ctor_count()); + EXPECT_EQ((T::move_ctor_policy_value == policy_nontrivial) ? 1 : 0, opt2->get_move_ctor_count()); + EXPECT_EQ(0U, opt2->get_copy_assignment_count()); + EXPECT_EQ(0U, opt2->get_move_assignment_count()); + EXPECT_EQ(0U, destructed); + // Empty to non-empty. Destructor is invoked. + opt1 = nullopt; + EXPECT_FALSE(opt1); + EXPECT_TRUE(opt2); + EXPECT_EQ((T::dtor_policy_value == policy_nontrivial) ? 1 : 0, destructed); + opt2 = std::move(opt1); + EXPECT_FALSE(opt1); + EXPECT_FALSE(opt2); + EXPECT_EQ((T::dtor_policy_value == policy_nontrivial) ? 2 : 0, destructed); + // Check opt1 counters. + EXPECT_EQ(((T::copy_ctor_policy_value == policy_nontrivial) && (T::move_ctor_policy_value == policy_deleted)) + ? 1 + : 0, + opt1->get_copy_ctor_count()); + EXPECT_EQ((T::move_ctor_policy_value == policy_nontrivial) ? 1 : 0, opt1->get_move_ctor_count()); + EXPECT_EQ(((T::copy_assignment_policy_value == policy_nontrivial) && + (T::move_assignment_policy_value == policy_deleted)) + ? 1 + : 0, + opt1->get_copy_assignment_count()); + EXPECT_EQ((T::move_assignment_policy_value == policy_nontrivial) ? 1 : 0, opt1->get_move_assignment_count()); + // Check opt2 counters. + EXPECT_EQ(((T::copy_ctor_policy_value == policy_nontrivial) && (T::move_ctor_policy_value == policy_deleted)) + ? 1 + : 0, + opt2->get_copy_ctor_count()); + EXPECT_EQ((T::move_ctor_policy_value == policy_nontrivial) ? 1 : 0, opt2->get_move_ctor_count()); + EXPECT_EQ(0U, opt2->get_copy_assignment_count()); + EXPECT_EQ(0U, opt2->get_move_assignment_count()); + } +}; +template +struct test_assignment_3 +{ + static_assert(std::is_copy_assignable::value == (CopyAssignmentPolicy != policy_deleted), ""); + static_assert(!std::is_copy_constructible::value, ""); + static_assert(std::is_move_assignable::value, ""); // requires either copy or move assignment + static_assert(!std::is_move_constructible::value, ""); + static_assert(!std::is_copy_constructible>::value, ""); + static_assert(!std::is_copy_assignable>::value, ""); + static_assert(!std::is_move_constructible>::value, ""); + static_assert(!std::is_move_assignable>::value, ""); + static void test() {} +}; +template +struct test_assignment_3 +{ + static_assert(!std::is_copy_assignable::value, ""); + static_assert(std::is_copy_constructible::value == (CopyCtorPolicy != policy_deleted), ""); + static_assert(!std::is_move_assignable::value, ""); + static_assert(std::is_move_constructible::value, ""); // requires either copy or move ctor + static_assert(std::is_copy_constructible>::value == (CopyCtorPolicy != policy_deleted), ""); + static_assert(!std::is_copy_assignable>::value, ""); + static_assert(std::is_move_constructible>::value, ""); // requires either copy or move ctor + static_assert(!std::is_move_assignable>::value, ""); + static void test() {} +}; +template +struct test_assignment_3 +{ + static_assert(!std::is_copy_assignable::value, ""); + static_assert(!std::is_copy_constructible::value, ""); + static_assert(!std::is_move_assignable::value, ""); + static_assert(!std::is_move_constructible::value, ""); + static_assert(!std::is_copy_constructible>::value, ""); + static_assert(!std::is_copy_assignable>::value, ""); + static_assert(!std::is_move_constructible>::value, ""); + static_assert(!std::is_move_assignable>::value, ""); + static void test() {} +}; + +TYPED_TEST(test_optional_combinations, assignment_3) +{ + test_assignment_3::test(); +}