Simplify ShouldPreferPassByValue and inline ShouldSemanticallyPreferPassByValue PiperOrigin-RevId: 1008243374
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index d4a7564..32c5f77 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h
@@ -84,8 +84,8 @@ // separate move-only overloads. template <class K, bool Value, bool ExtraConstraint = true> using LifetimeBoundK = std::bool_constant< - (ExtraConstraint || - !IsAllocMoveSameAsCopy<absl::remove_cvref_t<K>, Alloc>::value) && + (ExtraConstraint || !type_traits_internal::IsAllocMoveSameAsCopy< + absl::remove_cvref_t<K>, Alloc>::value) && HasValue<Value, std::conditional_t< policy_trait_element_is_owner<Policy>::value, std::false_type,
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 3a5a1ca..ee60645 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -2355,70 +2355,12 @@ TypeList<Policy, Hash, Eq, Alloc>>::type; }; -template <class T> -using HasTrivialCopyAndMove = - std::bool_constant<std::is_trivially_copy_constructible_v<T> && - std::is_trivially_move_constructible_v<T>>; - -// In principle, being trivially copy- and move-constructible is sufficient -// (though not necessary) for proving copy and move equivalence. However, we -// can't even test that for all types, because not all types are complete at the -// point of evaluation of this trait. Therefore, we restrict this trait to a -// subset of common types that we know to be complete at the point of evaluation -// of this trait. -template <class T> -inline constexpr bool kIsMoveSameAsCopy = HasTrivialCopyAndMove< - std::conditional_t<(!std::is_class_v<T> && !std::is_union_v<T>) || - std::is_same_v<T, std::type_index> || - std::is_same_v<T, absl::string_view>, - T, void>>::value; - -template <class T, class Traits> -inline constexpr bool kIsMoveSameAsCopy<std::basic_string_view<T, Traits>> = - std::is_trivially_copy_constructible_v<std::basic_string_view<T, Traits>>; - -template <class T, size_t N> -inline constexpr bool kIsMoveSameAsCopy<std::array<T, N>> = - kIsMoveSameAsCopy<T>; - -template <class T> -inline constexpr bool kIsMoveSameAsCopy<std::optional<T>> = - kIsMoveSameAsCopy<T>; - -template <class T1, class T2> -inline constexpr bool kIsMoveSameAsCopy<std::pair<T1, T2>> = - kIsMoveSameAsCopy<T1> && kIsMoveSameAsCopy<T2>; - -template <class... T> -inline constexpr bool kIsMoveSameAsCopy<std::tuple<T...>> = - (true && ... && kIsMoveSameAsCopy<T>); - -template <class... T> -inline constexpr bool kIsMoveSameAsCopy<std::variant<T...>> = - (true && ... && kIsMoveSameAsCopy<T>); - template <class T, class Alloc = std::allocator<T>> -using IsAllocMoveSameAsCopy = - std::bool_constant<kIsMoveSameAsCopy<std::conditional_t< - std::is_same_v<std::allocator<T>, typename std::allocator_traits< - Alloc>::template rebind_alloc<T>>, - T, void>>>; - -// This is a 'struct' instead of 'using' in order to avoid eager evaluation for -// callers that may be passing incomplete types. -template <class T> -struct AddConstRefIfExpensive - : std::conditional<type_traits_internal::ShouldPreferPassByValue<T>::value, - T, const T&> { - static_assert(std::is_same_v<remove_cvref_t<T>, T>, - "passed types must be unqualified"); -}; - -template <class T, class Alloc = std::allocator<T>> -using PreferredParamType = - typename std::conditional_t<IsAllocMoveSameAsCopy<T, Alloc>::value, - AddConstRefIfExpensive<T>, - std::add_rvalue_reference<T>>::type; +using DecayIfCheap = typename std::conditional_t< + type_traits_internal::IsAllocMoveSameAsCopy<T, Alloc>::value, + std::conditional<type_traits_internal::ShouldPreferPassByValue<T>::value, T, + std::add_lvalue_reference_t<std::add_const_t<T>>>, + std::add_rvalue_reference<T>>::type; // A SwissTable. // @@ -2586,9 +2528,9 @@ // Bit-fields cannot be moved, and types with trivial copy and move // constructors should also not be moved. template <class T> - using EnableElementMoveFrom = - std::bool_constant<std::is_pointer_v<T*> && - !IsAllocMoveSameAsCopy<T, Alloc>::value>; + using EnableElementMoveFrom = std::bool_constant< + std::is_pointer_v<T*> && + !type_traits_internal::IsAllocMoveSameAsCopy<T, Alloc>::value>; // RequiresNotInit is a workaround for gcc prior to 7.1. // See https://godbolt.org/g/Y4xsUh. @@ -3111,7 +3053,7 @@ // // flat_hash_map<std::string, int> s; // s.insert({"abc", 42}); - std::pair<iterator, bool> insert(PreferredParamType<init_type, Alloc> value) + std::pair<iterator, bool> insert(DecayIfCheap<init_type, Alloc> value) ABSL_ATTRIBUTE_LIFETIME_BOUND #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L requires(!IsLifetimeBoundAssignmentFrom<init_type>::value) @@ -3120,7 +3062,7 @@ return emplace(std::move(value)); } #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L - std::pair<iterator, bool> insert(PreferredParamType<init_type, Alloc> value + std::pair<iterator, bool> insert(DecayIfCheap<init_type, Alloc> value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS) ABSL_ATTRIBUTE_LIFETIME_BOUND requires(IsLifetimeBoundAssignmentFrom<init_type>::value) @@ -3155,7 +3097,7 @@ return insert(value).first; } - iterator insert(const_iterator, PreferredParamType<init_type, Alloc> value) + iterator insert(const_iterator, DecayIfCheap<init_type, Alloc> value) ABSL_ATTRIBUTE_LIFETIME_BOUND { return insert(std::move(value)).first; }
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 143d14e..a547193 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h
@@ -35,13 +35,18 @@ #ifndef ABSL_META_TYPE_TRAITS_H_ #define ABSL_META_TYPE_TRAITS_H_ +#include <limits.h> + #include <array> #include <cstddef> #include <functional> +#include <memory> +#include <optional> #include <string> #include <string_view> #include <tuple> #include <type_traits> +#include <typeindex> #include <utility> #include <variant> #include <vector> @@ -66,6 +71,10 @@ namespace absl { ABSL_NAMESPACE_BEGIN +#ifndef ABSL_USES_STD_STRING_VIEW +class string_view; +#endif + namespace type_traits_internal { template <typename... Ts> @@ -636,6 +645,62 @@ IsView<absl::remove_cvref_t<T>>>; template <class T> +using HasTrivialCopyAndMove = + std::bool_constant<std::is_trivially_copy_constructible_v<T> && + std::is_trivially_move_constructible_v<T>>; + +// In principle, being trivially copy- and move-constructible is sufficient +// (though not necessary) for proving copy and move equivalence. However, we +// can't even test that for all types, because not all types are complete at the +// point of evaluation of this trait. Therefore, we restrict this trait to a +// subset of common types that we know to be complete at the point of evaluation +// of this trait. +template <class T> +inline constexpr bool kIsMoveSameAsCopy = + HasTrivialCopyAndMove<std::conditional_t< + std::is_class_v<T> || std::is_union_v<T>, void, T>>::value; + +template <class T, class Traits> +inline constexpr bool kIsMoveSameAsCopy<std::basic_string_view<T, Traits>> = + std::is_trivially_copy_constructible_v<std::basic_string_view<T, Traits>>; + +template <class T, size_t N> +inline constexpr bool kIsMoveSameAsCopy<std::array<T, N>> = + kIsMoveSameAsCopy<T>; + +template <class T> +inline constexpr bool kIsMoveSameAsCopy<std::optional<T>> = + kIsMoveSameAsCopy<T>; + +template <class T1, class T2> +inline constexpr bool kIsMoveSameAsCopy<std::pair<T1, T2>> = + kIsMoveSameAsCopy<T1> && kIsMoveSameAsCopy<T2>; + +template <class... T> +inline constexpr bool kIsMoveSameAsCopy<std::tuple<T...>> = + (true && ... && kIsMoveSameAsCopy<T>); + +template <> +inline constexpr bool kIsMoveSameAsCopy<std::type_index> = true; + +template <class... T> +inline constexpr bool kIsMoveSameAsCopy<std::variant<T...>> = + (true && ... && kIsMoveSameAsCopy<T>); + +#ifndef ABSL_USES_STD_STRING_VIEW +template <> +inline constexpr bool kIsMoveSameAsCopy<absl::string_view> = + kIsMoveSameAsCopy<std::string_view>; +#endif + +template <class T, class Alloc> +using IsAllocMoveSameAsCopy = std::bool_constant< + type_traits_internal::kIsMoveSameAsCopy<std::conditional_t< + std::is_same_v<std::allocator<T>, typename std::allocator_traits< + Alloc>::template rebind_alloc<T>>, + T, void>>>; + +template <class T> using ShouldStructurallyPreferPassByValue = std::bool_constant< // Check if size is a power of 2, to ensure cheap calls. (sizeof(T) & (sizeof(T) - 1)) == 0 && @@ -645,20 +710,15 @@ alignof(T) * 2 >= sizeof(T) && sizeof(T) <= sizeof(std::pair<void*, size_t>)>; -template <class T> -using ShouldSemanticallyPreferPassByValue = - std::bool_constant<std::is_trivially_move_constructible_v<T> && - std::is_trivially_destructible_v<T>>; - // Checks the given type to see if it is a good candidate for accepting by value // instead of by rvalue-reference. This includes both a semantic constraint // (to ensure correctness), as well as a structural constraint (to ensure // performance). template <class T> -using ShouldPreferPassByValue = - std::bool_constant<ShouldSemanticallyPreferPassByValue<T>::value && - ShouldStructurallyPreferPassByValue<std::conditional_t< - std::is_void_v<T>, char, T>>::value>; +using ShouldPreferPassByValue = ShouldStructurallyPreferPassByValue< + std::conditional_t<std::is_trivially_move_constructible_v<T> && + std::is_trivially_destructible_v<T>, + T, char[UINT_MAX]>>; } // namespace type_traits_internal