[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] odd/move_semantics 3ce1a0b3 5/5: Attempt to ascertai
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] odd/move_semantics 3ce1a0b3 5/5: Attempt to ascertain whether moving will use move semantics |
Date: |
Wed, 20 Jul 2022 19:50:38 -0400 (EDT) |
branch: odd/move_semantics
commit 3ce1a0b3ca5828437ff2aef71d5709b5fead54d0
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Attempt to ascertain whether moving will use move semantics
---
sandbox_test.cpp | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 299 insertions(+)
diff --git a/sandbox_test.cpp b/sandbox_test.cpp
index 6baaf4c8..3bef00be 100644
--- a/sandbox_test.cpp
+++ b/sandbox_test.cpp
@@ -21,9 +21,308 @@
#include "pchfile.hpp"
+#include "miscellany.hpp" // stifle_unused_warning()
#include "test_tools.hpp"
+#include <type_traits>
+
+/// Induce ambiguity between a class's copy and move ctors.
+///
+/// If class T has both a copy and a move ctor, both of which
+/// can be considered in overload resolution, then this:
+/// T t {ambiguator<T>{}};
+/// is an error because neither ctor is better than the other.
+/// As clang says:
+/// error: call to constructor of 'T' is ambiguous
+/// T t {ambiguator<T>{}};
+/// ^ ~~~~~~~~~~~~~~~~~
+/// note: candidate constructor (the implicit copy constructor)
+/// note: candidate constructor (the implicit move constructor)
+/// Detecting that condition in this way:
+/// bool equiplausible = !std::is_constructible_v<T, ambiguator<T>>
+/// is not an error.
+
+template<typename T>
+struct ambiguator
+{
+ operator T const&();
+ operator T&&();
+};
+
+template<typename T> bool equiplausible_construct()
+{
+ return !std::is_constructible_v<T,ambiguator<T>>;
+}
+
+template<typename T> bool equiplausible_assign()
+{
+ return !std::is_assignable_v<T,ambiguator<T>>;
+}
+
+template<typename T> bool copyable()
+{
+ return std::is_copy_constructible_v<T>;
+}
+
+template<typename T> bool a_moveable() // assign-moveable
+{
+ return std::is_move_assignable_v<T> && equiplausible_assign<T>();
+}
+
+template<typename T> bool c_moveable() // copy-moveable
+{
+ return std::is_move_constructible_v<T> && equiplausible_construct<T>();
+}
+
+struct can_move
+{
+};
+
+can_move moveable_instance;
+
+struct no_can_move
+{
+ no_can_move() = default;
+ ~no_can_move() = default;
+ no_can_move(no_can_move const&) = default;
+ no_can_move(no_can_move&&) = delete;
+ no_can_move& operator=(no_can_move const&) = default;
+ no_can_move& operator=(no_can_move&&) = delete;
+};
+
+/// Classes for unit testing.
+///
+/// Class names are generally one capital letter and four digits,
+/// signifying {cctor, mctor, cassign, massign}:
+/// 0 = user declared as defaulted
+/// 1 = not user declared
+/// 2 = explicitly defaulted, but inaccessible
+/// 3 = explicitly deleted
+/// 4 = explicitly defaulted, but implicitly deleted [move only]
+
+struct A0000
+{
+ A0000() = default;
+ ~A0000() = default;
+ A0000(A0000 const&) = default;
+ A0000(A0000&&) = default;
+ A0000& operator=(A0000 const&) = default;
+ A0000& operator=(A0000&&) = default;
+};
+
+struct A0101
+{
+ A0101() = default;
+ ~A0101() = default;
+ A0101(A0101 const&) = default;
+// A0101(A0101&&) // not declared
+ A0101& operator=(A0101 const&) = default;
+// A0101& operator=(A0101&&) // not declared
+};
+
+struct A0202
+{
+ A0202() = default;
+ ~A0202() = default;
+ A0202(A0202 const&) = default;
+ A0202& operator=(A0202 const&) = default;
+ protected:
+ A0202(A0202&&) = default;
+ A0202& operator=(A0202&&) = default;
+};
+
+struct A0303
+{
+ A0303() = default;
+ ~A0303() = default;
+ A0303(A0303 const&) = default;
+ A0303(A0303&&) = delete;
+ A0303& operator=(A0303 const&) = default;
+ A0303& operator=(A0303&&) = delete;
+};
+
+#if defined LMI_CLANG
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdefaulted-function-deleted"
+#endif // defined LMI_CLANG
+struct A0404 : public no_can_move
+{
+ A0404() = default;
+ ~A0404() = default;
+ A0404(A0404 const&) = default;
+ A0404(A0404&&) = default;
+ A0404& operator=(A0404 const&) = default;
+ A0404& operator=(A0404&&) = default;
+};
+
+struct A0505
+{
+ A0505() = default;
+ ~A0505() = default;
+ A0505(A0505 const&) = default;
+ A0505(A0505&&) = default;
+ A0505& operator=(A0505 const&) = default;
+ A0505& operator=(A0505&&) = default;
+// int const i_ {}; // inhibits assignment, not construction.
+ can_move& cm_ {moveable_instance};
+};
+#if defined LMI_CLANG
+# pragma clang diagnostic pop
+#endif // defined LMI_CLANG
+
+struct A3030
+{
+ A3030() = default;
+ ~A3030() = default;
+ A3030(A3030 const&) = delete;
+ A3030(A3030&&) = default;
+ A3030& operator=(A3030 const&) = delete;
+ A3030& operator=(A3030&&) = default;
+};
+
+void test_classes()
+{
+// 0 = user declared as defaulted
+// 1 = not user declared
+// 2 = explicitly defaulted, but inaccessible
+// 3 = explicitly deleted
+// 4 = unmoveable due to base
+// 5 = unmoveable due to const member
+//
+// Hypotheses:
+// r ≡ t
+// x ≡ q ∧ r
+// y ≡ s ∧ t
+// z ≡ q
+//
+// 0000 0101 0202 0303 0404 0505 3030
+// + + + + + + - p: is_copy_constructible
+// + + - - + + + q: is_move_constructible
+// + - + + - + + r: equiplausible_construct
+// + + - - + - + s: is_move_assignable
+// + - + + - + + t: equiplausible_assign
+// + - - - - + + x: move construct has move semantics
+// + - - - - - + y: move assign has move semantics
+// + + - - + + + z: move compiles without error
+
+ LMI_TEST( std::is_copy_constructible_v<A0000>);
+ LMI_TEST( std::is_move_constructible_v<A0000>);
+ LMI_TEST((!std::is_constructible_v<A0000,ambiguator<A0000>>));
+ LMI_TEST( copyable<A0000>());
+ LMI_TEST( c_moveable<A0000>());
+ LMI_TEST( a_moveable<A0000>());
+
+ LMI_TEST( std::is_copy_constructible_v<A0000>);
+ LMI_TEST( std::is_copy_constructible_v<A0101>);
+ LMI_TEST( std::is_copy_constructible_v<A0202>);
+ LMI_TEST( std::is_copy_constructible_v<A0303>);
+ LMI_TEST( std::is_copy_constructible_v<A0404>);
+ LMI_TEST( std::is_copy_constructible_v<A0505>);
+ LMI_TEST(!std::is_copy_constructible_v<A3030>);
+
+ LMI_TEST( std::is_move_constructible_v<A0000>);
+ LMI_TEST( std::is_move_constructible_v<A0101>);
+ LMI_TEST(!std::is_move_constructible_v<A0202>);
+ LMI_TEST(!std::is_move_constructible_v<A0303>);
+ LMI_TEST( std::is_move_constructible_v<A0404>);
+ LMI_TEST( std::is_move_constructible_v<A0505>);
+ LMI_TEST( std::is_move_constructible_v<A3030>);
+
+ LMI_TEST( std::is_move_assignable_v<A0000>);
+ LMI_TEST( std::is_move_assignable_v<A0101>);
+ LMI_TEST(!std::is_move_assignable_v<A0202>);
+ LMI_TEST(!std::is_move_assignable_v<A0303>);
+ LMI_TEST( std::is_move_assignable_v<A0404>);
+ LMI_TEST(!std::is_move_assignable_v<A0505>);
+ LMI_TEST( std::is_move_assignable_v<A3030>);
+
+ LMI_TEST((!std::is_constructible_v<A0000,ambiguator<A0000>>));
+ LMI_TEST(( std::is_constructible_v<A0101,ambiguator<A0101>>));
+ LMI_TEST((!std::is_constructible_v<A0202,ambiguator<A0202>>));
+ LMI_TEST((!std::is_constructible_v<A0303,ambiguator<A0303>>));
+ LMI_TEST(( std::is_constructible_v<A0404,ambiguator<A0404>>));
+ LMI_TEST((!std::is_constructible_v<A0505,ambiguator<A0505>>));
+ LMI_TEST((!std::is_constructible_v<A3030,ambiguator<A3030>>));
+
+ LMI_TEST( equiplausible_construct<A0000>());
+ LMI_TEST(!equiplausible_construct<A0101>());
+ LMI_TEST( equiplausible_construct<A0202>());
+ LMI_TEST( equiplausible_construct<A0303>());
+ LMI_TEST(!equiplausible_construct<A0404>());
+ LMI_TEST( equiplausible_construct<A0505>());
+ LMI_TEST( equiplausible_construct<A3030>());
+
+ LMI_TEST( equiplausible_assign<A0000>());
+ LMI_TEST(!equiplausible_assign<A0101>());
+ LMI_TEST( equiplausible_assign<A0202>());
+ LMI_TEST( equiplausible_assign<A0303>());
+ LMI_TEST(!equiplausible_assign<A0404>());
+ LMI_TEST( equiplausible_assign<A0505>());
+ LMI_TEST( equiplausible_assign<A3030>());
+
+ LMI_TEST( copyable<A0000>());
+ LMI_TEST( copyable<A0101>());
+ LMI_TEST( copyable<A0202>());
+ LMI_TEST( copyable<A0303>());
+ LMI_TEST( copyable<A0404>());
+ LMI_TEST( copyable<A0505>());
+ LMI_TEST(!copyable<A3030>());
+
+ LMI_TEST( c_moveable<A0000>());
+ LMI_TEST(!c_moveable<A0101>());
+ LMI_TEST(!c_moveable<A0202>());
+ LMI_TEST(!c_moveable<A0303>());
+ LMI_TEST(!c_moveable<A0404>());
+ LMI_TEST( c_moveable<A0505>()); // move-constructible; not move-assignable
+ LMI_TEST( c_moveable<A3030>());
+
+ LMI_TEST( a_moveable<A0000>());
+ LMI_TEST(!a_moveable<A0101>());
+ LMI_TEST(!a_moveable<A0202>());
+ LMI_TEST(!a_moveable<A0303>());
+ LMI_TEST(!a_moveable<A0404>());
+ LMI_TEST(!a_moveable<A0505>()); // move-constructible; not move-assignable
+ LMI_TEST( a_moveable<A3030>());
+
+ A0000 a0000 {};
+ A0101 a0101 {};
+ A0202 a0202 {};
+ A0303 a0303 {};
+ A0404 a0404 {};
+ A0505 a0505 {};
+ A3030 a3030 {};
+
+ A0000 b0000 {std::move(a0000)};
+ A0101 b0101 {std::move(a0101)};
+// A0202 b0202 {std::move(a0202)}; // protected
+// A0303 b0303 {std::move(a0303)}; // deleted
+ A0404 b0404 {std::move(a0404)};
+ A0505 b0505 {std::move(a0505)};
+ A3030 b3030 {std::move(a3030)};
+
+ A0000 c0000; c0000 = std::move(a0000);
+ A0101 c0101; c0101 = std::move(a0101);
+// A0202 c0202; c0202 = std::move(a0202); // protected
+// A0303 c0303; c0303 = std::move(a0303); // deleted
+ A0404 c0404; c0404 = std::move(a0404);
+// A0505 c0505; c0505 = std::move(a0505); // copy= and move= implicitly
deleted
+ A3030 c3030; c3030 = std::move(a3030);
+
+ stifle_unused_warning(a0202);
+ stifle_unused_warning(a0303);
+
+ stifle_unused_warning(b0000);
+ stifle_unused_warning(b0101);
+// stifle_unused_warning(b0202);
+// stifle_unused_warning(b0303);
+ stifle_unused_warning(b0404);
+ stifle_unused_warning(b0505);
+ stifle_unused_warning(b3030);
+}
+
int test_main(int, char*[])
{
+ test_classes();
+
return 0;
}