summaryrefslogtreecommitdiff
path: root/absl/meta
diff options
context:
space:
mode:
Diffstat (limited to 'absl/meta')
-rw-r--r--absl/meta/type_traits.h61
-rw-r--r--absl/meta/type_traits_test.cc81
2 files changed, 142 insertions, 0 deletions
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 8a788dea..b1b14914 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -485,4 +485,65 @@ inline void AssertHashEnabled() {
} // namespace absl
+// An internal namespace that is required to implement the C++17 swap traits.
+//
+// NOTE: This is its own top-level namespace to avoid subtleties due to
+// functions named "swap" that may appear in the absl namespace.
+namespace absl_internal_swap {
+
+using std::swap;
+
+template <class T>
+using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>()));
+
+// NOTE: This dance with the default template parameter is for MSVC.
+template <class T,
+ class IsNoexcept = std::integral_constant<
+ bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>>
+using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type;
+
+// IsSwappable
+//
+// Determines whether the standard swap idiom is a valid expression for
+// arguments of type `T`.
+template <class T>
+struct IsSwappable
+ : absl::type_traits_internal::is_detected<IsSwappableImpl, T> {};
+
+// IsNothrowSwappable
+//
+// Determines whether the standard swap idiom is a valid expression for
+// arguments of type `T` and is noexcept.
+template <class T>
+struct IsNothrowSwappable
+ : absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {};
+
+// Swap()
+//
+// Performs the swap idiom from a namespace with no additional `swap` overloads.
+template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0>
+void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) {
+ swap(lhs, rhs);
+}
+
+} // namespace absl_internal_swap
+
+namespace absl {
+namespace type_traits_internal {
+
+// Make the swap-related traits/function accessible from this namespace.
+using absl_internal_swap::IsNothrowSwappable;
+using absl_internal_swap::IsSwappable;
+using absl_internal_swap::Swap;
+
+// StdSwapIsUnconstrained
+//
+// Some standard library implementations are broken in that they do not
+// constrain `std::swap`. This will effectively tell us if we are dealing with
+// one of those implementations.
+using StdSwapIsUnconstrained = IsSwappable<void()>;
+
+} // namespace type_traits_internal
+} // namespace absl
+
#endif // ABSL_META_TYPE_TRAITS_H_
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index 29a6db69..912336e9 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -953,4 +953,85 @@ TEST(TypeTraitsTest, IsMoveAssignable) {
#endif // _LIBCPP_VERSION
}
+namespace adl_namespace {
+
+struct DeletedSwap {
+};
+
+void swap(DeletedSwap&, DeletedSwap&) = delete;
+
+struct SpecialNoexceptSwap {
+ SpecialNoexceptSwap(SpecialNoexceptSwap&&) {}
+ SpecialNoexceptSwap& operator=(SpecialNoexceptSwap&&) { return *this; }
+ ~SpecialNoexceptSwap() = default;
+};
+
+void swap(SpecialNoexceptSwap&, SpecialNoexceptSwap&) noexcept {}
+
+} // namespace adl_namespace
+
+TEST(TypeTraitsTest, IsSwappable) {
+ using absl::type_traits_internal::IsSwappable;
+ using absl::type_traits_internal::StdSwapIsUnconstrained;
+
+ EXPECT_TRUE(IsSwappable<int>::value);
+
+ struct S {};
+ EXPECT_TRUE(IsSwappable<S>::value);
+
+ struct NoConstruct {
+ NoConstruct(NoConstruct&&) = delete;
+ NoConstruct& operator=(NoConstruct&&) { return *this; }
+ ~NoConstruct() = default;
+ };
+
+ EXPECT_EQ(IsSwappable<NoConstruct>::value, StdSwapIsUnconstrained::value);
+ struct NoAssign {
+ NoAssign(NoAssign&&) {}
+ NoAssign& operator=(NoAssign&&) = delete;
+ ~NoAssign() = default;
+ };
+
+ EXPECT_EQ(IsSwappable<NoAssign>::value, StdSwapIsUnconstrained::value);
+
+ EXPECT_FALSE(IsSwappable<adl_namespace::DeletedSwap>::value);
+
+ EXPECT_TRUE(IsSwappable<adl_namespace::SpecialNoexceptSwap>::value);
+}
+
+TEST(TypeTraitsTest, IsNothrowSwappable) {
+ using absl::type_traits_internal::IsNothrowSwappable;
+ using absl::type_traits_internal::StdSwapIsUnconstrained;
+
+ EXPECT_TRUE(IsNothrowSwappable<int>::value);
+
+ struct NonNoexceptMoves {
+ NonNoexceptMoves(NonNoexceptMoves&&) {}
+ NonNoexceptMoves& operator=(NonNoexceptMoves&&) { return *this; }
+ ~NonNoexceptMoves() = default;
+ };
+
+ EXPECT_FALSE(IsNothrowSwappable<NonNoexceptMoves>::value);
+
+ struct NoConstruct {
+ NoConstruct(NoConstruct&&) = delete;
+ NoConstruct& operator=(NoConstruct&&) { return *this; }
+ ~NoConstruct() = default;
+ };
+
+ EXPECT_FALSE(IsNothrowSwappable<NoConstruct>::value);
+
+ struct NoAssign {
+ NoAssign(NoAssign&&) {}
+ NoAssign& operator=(NoAssign&&) = delete;
+ ~NoAssign() = default;
+ };
+
+ EXPECT_FALSE(IsNothrowSwappable<NoAssign>::value);
+
+ EXPECT_FALSE(IsNothrowSwappable<adl_namespace::DeletedSwap>::value);
+
+ EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value);
+}
+
} // namespace