lmi-commits
[Top][All Lists]
Advanced

[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;
 }



reply via email to

[Prev in Thread] Current Thread [Next in Thread]