about summary refs log tree commit diff
path: root/absl/meta
diff options
context:
space:
mode:
Diffstat (limited to 'absl/meta')
-rw-r--r--absl/meta/CMakeLists.txt2
-rw-r--r--absl/meta/type_traits.h109
-rw-r--r--absl/meta/type_traits_test.cc191
3 files changed, 299 insertions, 3 deletions
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index f866e54e9271..672ead2fd0a5 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -42,6 +42,8 @@ absl_cc_test(
 absl_cc_library(
   NAME
     meta
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::type_traits
   PUBLIC
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index a8068e315ed3..c08e3754689b 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -43,8 +43,39 @@
 
 namespace absl {
 
+// Defined and documented later on in this file.
+template <typename T>
+struct is_trivially_move_assignable;
+
 namespace type_traits_internal {
 
+// Silence MSVC warnings about the destructor being defined as deleted.
+#if defined(_MSC_VER) && !defined(__GNUC__)
+#pragma warning(push)
+#pragma warning(disable : 4624)
+#endif  // defined(_MSC_VER) && !defined(__GNUC__)
+
+template <class T>
+union SingleMemberUnion {
+  T t;
+};
+
+// Restore the state of the destructor warning that was silenced above.
+#if defined(_MSC_VER) && !defined(__GNUC__)
+#pragma warning(pop)
+#endif  // defined(_MSC_VER) && !defined(__GNUC__)
+
+template <class T>
+struct IsTriviallyMoveAssignableReference : std::false_type {};
+
+template <class T>
+struct IsTriviallyMoveAssignableReference<T&>
+    : absl::is_trivially_move_assignable<T>::type {};
+
+template <class T>
+struct IsTriviallyMoveAssignableReference<T&&>
+    : absl::is_trivially_move_assignable<T>::type {};
+
 template <typename... Ts>
 struct VoidTImpl {
   using type = void;
@@ -275,6 +306,40 @@ struct is_trivially_default_constructible
 #endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
 };
 
+// is_trivially_move_constructible()
+//
+// Determines whether the passed type `T` is trivially move constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_move_constructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `T obj(declval<T>());` needs to be well-formed and not call any
+// nontrivial operation.  Nontrivially destructible types will cause the
+// expression to be nontrivial.
+template <typename T>
+struct is_trivially_move_constructible
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value,
+          std::is_move_constructible<
+              type_traits_internal::SingleMemberUnion<T>>,
+          std::is_reference<T>>::type::type {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_move_constructible<T>::value ==
+      is_trivially_move_constructible::value;
+  static_assert(compliant || std::is_trivially_move_constructible<T>::value,
+                "Not compliant with std::is_trivially_move_constructible; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_move_constructible<T>::value,
+                "Not compliant with std::is_trivially_move_constructible; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
 // is_trivially_copy_constructible()
 //
 // Determines whether the passed type `T` is trivially copy constructible.
@@ -290,9 +355,11 @@ struct is_trivially_default_constructible
 // expression to be nontrivial.
 template <typename T>
 struct is_trivially_copy_constructible
-    : std::integral_constant<bool, __has_trivial_copy(T) &&
-                                   std::is_copy_constructible<T>::value &&
-                                   is_trivially_destructible<T>::value> {
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value,
+          std::is_copy_constructible<
+              type_traits_internal::SingleMemberUnion<T>>,
+          std::is_lvalue_reference<T>>::type::type {
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
  private:
   static constexpr bool compliant =
@@ -307,6 +374,42 @@ struct is_trivially_copy_constructible
 #endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
 };
 
+// is_trivially_move_assignable()
+//
+// Determines whether the passed type `T` is trivially move assignable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_move_assignable()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `is_assignable<T, U>::value` is `true` if the expression
+// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
+// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
+// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
+// `is_trivially_assignable<T&, T>`.
+template <typename T>
+struct is_trivially_move_assignable
+    : std::conditional<
+          std::is_object<T>::value && !std::is_array<T>::value,
+          std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>,
+          type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type::
+          type {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+ private:
+  static constexpr bool compliant =
+      std::is_trivially_move_assignable<T>::value ==
+      is_trivially_move_assignable::value;
+  static_assert(compliant || std::is_trivially_move_assignable<T>::value,
+                "Not compliant with std::is_trivially_move_assignable; "
+                "Standard: false, Implementation: true");
+  static_assert(compliant || !std::is_trivially_move_assignable<T>::value,
+                "Not compliant with std::is_trivially_move_assignable; "
+                "Standard: true, Implementation: false");
+#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+};
+
 // is_trivially_copy_assignable()
 //
 // Determines whether the passed type `T` is trivially copy assignable.
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index d3ead6d05158..82b1be51065e 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -214,6 +214,29 @@ class DeletedDefaultCtor {
   int n_;
 };
 
+class TrivialMoveCtor {
+ public:
+  explicit TrivialMoveCtor(int n) : n_(n) {}
+  TrivialMoveCtor(TrivialMoveCtor&&) = default;
+  TrivialMoveCtor& operator=(const TrivialMoveCtor& t) {
+    n_ = t.n_;
+    return *this;
+  }
+
+ private:
+  int n_;
+};
+
+class NontrivialMoveCtor {
+ public:
+  explicit NontrivialMoveCtor(int n) : n_(n) {}
+  NontrivialMoveCtor(NontrivialMoveCtor&& t) noexcept : n_(t.n_) {}
+  NontrivialMoveCtor& operator=(const NontrivialMoveCtor&) = default;
+
+ private:
+  int n_;
+};
+
 class TrivialCopyCtor {
  public:
   explicit TrivialCopyCtor(int n) : n_(n) {}
@@ -247,6 +270,29 @@ class DeletedCopyCtor {
   int n_;
 };
 
+class TrivialMoveAssign {
+ public:
+  explicit TrivialMoveAssign(int n) : n_(n) {}
+  TrivialMoveAssign(const TrivialMoveAssign& t) : n_(t.n_) {}
+  TrivialMoveAssign& operator=(TrivialMoveAssign&&) = default;
+  ~TrivialMoveAssign() {}  // can have nontrivial destructor
+ private:
+  int n_;
+};
+
+class NontrivialMoveAssign {
+ public:
+  explicit NontrivialMoveAssign(int n) : n_(n) {}
+  NontrivialMoveAssign(const NontrivialMoveAssign&) = default;
+  NontrivialMoveAssign& operator=(NontrivialMoveAssign&& t) noexcept {
+    n_ = t.n_;
+    return *this;
+  }
+
+ private:
+  int n_;
+};
+
 class TrivialCopyAssign {
  public:
   explicit TrivialCopyAssign(int n) : n_(n) {}
@@ -484,6 +530,79 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) {
 #endif
 }
 
+TEST(TypeTraitsTest, TestTrivialMoveCtor) {
+  // Verify that arithmetic types and pointers have trivial move
+  // constructors.
+  EXPECT_TRUE(absl::is_trivially_move_constructible<bool>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<signed char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<wchar_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<int>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned int>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<int16_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<uint16_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<int64_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<uint64_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<float>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<double>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<long double>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<const std::string*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<const Trivial*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string**>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial**>::value);
+
+  // Reference types
+  EXPECT_TRUE(absl::is_trivially_move_constructible<int&>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<int&&>::value);
+
+  // types with compiler generated move ctors
+  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial>::value);
+  EXPECT_TRUE(absl::is_trivially_move_constructible<TrivialMoveCtor>::value);
+
+  // Verify that types without them (i.e. nontrivial or deleted) are not.
+  EXPECT_FALSE(
+      absl::is_trivially_move_constructible<NontrivialCopyCtor>::value);
+  EXPECT_FALSE(absl::is_trivially_move_constructible<DeletedCopyCtor>::value);
+  EXPECT_FALSE(
+      absl::is_trivially_move_constructible<NonCopyableOrMovable>::value);
+
+#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE
+  // type with nontrivial destructor are nontrivial move construbtible
+  EXPECT_FALSE(
+      absl::is_trivially_move_constructible<NontrivialDestructor>::value);
+#endif
+
+  // types with vtables
+  EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value);
+
+  // Verify that simple_pair of such types is trivially move constructible
+  EXPECT_TRUE(
+      (absl::is_trivially_move_constructible<simple_pair<int, char*>>::value));
+  EXPECT_TRUE((
+      absl::is_trivially_move_constructible<simple_pair<int, Trivial>>::value));
+  EXPECT_TRUE((absl::is_trivially_move_constructible<
+               simple_pair<int, TrivialMoveCtor>>::value));
+
+  // Verify that types without trivial move constructors are
+  // correctly marked as such.
+  EXPECT_FALSE(absl::is_trivially_move_constructible<std::string>::value);
+  EXPECT_FALSE(absl::is_trivially_move_constructible<std::vector<int>>::value);
+
+  // Verify that simple_pairs of types without trivial move constructors
+  // are not marked as trivial.
+  EXPECT_FALSE((absl::is_trivially_move_constructible<
+                simple_pair<int, std::string>>::value));
+  EXPECT_FALSE((absl::is_trivially_move_constructible<
+                simple_pair<std::string, int>>::value));
+
+  // Verify that arrays are not
+  using int10 = int[10];
+  EXPECT_FALSE(absl::is_trivially_move_constructible<int10>::value);
+}
+
 TEST(TypeTraitsTest, TestTrivialCopyCtor) {
   // Verify that arithmetic types and pointers have trivial copy
   // constructors.
@@ -508,6 +627,10 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) {
   EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string**>::value);
   EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial**>::value);
 
+  // Reference types
+  EXPECT_TRUE(absl::is_trivially_copy_constructible<int&>::value);
+  EXPECT_FALSE(absl::is_trivially_copy_constructible<int&&>::value);
+
   // types with compiler generated copy ctors
   EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial>::value);
   EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivialCopyCtor>::value);
@@ -555,6 +678,74 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) {
   EXPECT_FALSE(absl::is_trivially_copy_constructible<int10>::value);
 }
 
+TEST(TypeTraitsTest, TestTrivialMoveAssign) {
+  // Verify that arithmetic types and pointers have trivial move
+  // assignment operators.
+  EXPECT_TRUE(absl::is_trivially_move_assignable<bool>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<signed char>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<wchar_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<int>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned int>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<int16_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<uint16_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<int64_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<uint64_t>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<float>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<double>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<long double>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<const std::string*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<const Trivial*>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string**>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial**>::value);
+
+  // const qualified types are not assignable
+  EXPECT_FALSE(absl::is_trivially_move_assignable<const int>::value);
+
+  // types with compiler generated move assignment
+  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<TrivialMoveAssign>::value);
+
+  // Verify that types without them (i.e. nontrivial or deleted) are not.
+  EXPECT_FALSE(absl::is_trivially_move_assignable<NontrivialCopyAssign>::value);
+  EXPECT_FALSE(absl::is_trivially_move_assignable<DeletedCopyAssign>::value);
+  EXPECT_FALSE(absl::is_trivially_move_assignable<NonCopyableOrMovable>::value);
+
+  // types with vtables
+  EXPECT_FALSE(absl::is_trivially_move_assignable<Base>::value);
+
+  // Verify that simple_pair is trivially assignable
+  EXPECT_TRUE(
+      (absl::is_trivially_move_assignable<simple_pair<int, char*>>::value));
+  EXPECT_TRUE(
+      (absl::is_trivially_move_assignable<simple_pair<int, Trivial>>::value));
+  EXPECT_TRUE((absl::is_trivially_move_assignable<
+               simple_pair<int, TrivialMoveAssign>>::value));
+
+  // Verify that types not trivially move assignable are
+  // correctly marked as such.
+  EXPECT_FALSE(absl::is_trivially_move_assignable<std::string>::value);
+  EXPECT_FALSE(absl::is_trivially_move_assignable<std::vector<int>>::value);
+
+  // Verify that simple_pairs of types not trivially move assignable
+  // are not marked as trivial.
+  EXPECT_FALSE((absl::is_trivially_move_assignable<
+                simple_pair<int, std::string>>::value));
+  EXPECT_FALSE((absl::is_trivially_move_assignable<
+                simple_pair<std::string, int>>::value));
+
+  // Verify that arrays are not trivially move assignable
+  using int10 = int[10];
+  EXPECT_FALSE(absl::is_trivially_move_assignable<int10>::value);
+
+  // Verify that references are handled correctly
+  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&&>::value);
+  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&>::value);
+}
+
 TEST(TypeTraitsTest, TestTrivialCopyAssign) {
   // Verify that arithmetic types and pointers have trivial copy
   // assignment operators.