diff options
Diffstat (limited to 'absl/meta/type_traits.h')
-rw-r--r-- | absl/meta/type_traits.h | 421 |
1 files changed, 48 insertions, 373 deletions
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index b1656c39..cf71164b 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -39,14 +39,9 @@ #include <functional> #include <type_traits> +#include "absl/base/attributes.h" #include "absl/base/config.h" -// MSVC constructibility traits do not detect destructor properties and so our -// implementations should not use them as a source-of-truth. -#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) -#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 -#endif - // Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17 // feature. #if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) @@ -58,57 +53,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// Defined and documented later on in this file. -template <typename T> -struct is_trivially_destructible; - -// 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 IsTriviallyMoveConstructibleObject - : std::integral_constant< - bool, std::is_move_constructible< - type_traits_internal::SingleMemberUnion<T>>::value && - absl::is_trivially_destructible<T>::value> {}; - -template <class T> -struct IsTriviallyCopyConstructibleObject - : std::integral_constant< - bool, std::is_copy_constructible< - type_traits_internal::SingleMemberUnion<T>>::value && - absl::is_trivially_destructible<T>::value> {}; - -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; @@ -157,39 +103,8 @@ template <class To, template <class...> class Op, class... Args> struct is_detected_convertible : is_detected_convertible_impl<void, To, Op, Args...>::type {}; -template <typename T> -using IsCopyAssignableImpl = - decltype(std::declval<T&>() = std::declval<const T&>()); - -template <typename T> -using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>()); - } // namespace type_traits_internal -// MSVC 19.20 has a regression that causes our workarounds to fail, but their -// std forms now appear to be compliant. -#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920) - -template <typename T> -using is_copy_assignable = std::is_copy_assignable<T>; - -template <typename T> -using is_move_assignable = std::is_move_assignable<T>; - -#else - -template <typename T> -struct is_copy_assignable : type_traits_internal::is_detected< - type_traits_internal::IsCopyAssignableImpl, T> { -}; - -template <typename T> -struct is_move_assignable : type_traits_internal::is_detected< - type_traits_internal::IsMoveAssignableImpl, T> { -}; - -#endif - // void_t() // // Ignores the type of any its arguments and returns `void`. In general, this @@ -270,246 +185,29 @@ struct is_function bool, !(std::is_reference<T>::value || std::is_const<typename std::add_const<T>::type>::value)> {}; +// is_copy_assignable() +// is_move_assignable() // is_trivially_destructible() -// -// Determines whether the passed type `T` is trivially destructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_destructible()` 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: the extensions (__has_trivial_xxx) are implemented in gcc (version >= -// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always -// be present. These extensions are documented at -// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits. -template <typename T> -struct is_trivially_destructible -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE - : std::is_trivially_destructible<T> { -#else - : std::integral_constant<bool, __has_trivial_destructor(T) && - std::is_destructible<T>::value> { -#endif -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE - private: - static constexpr bool compliant = std::is_trivially_destructible<T>::value == - is_trivially_destructible::value; - static_assert(compliant || std::is_trivially_destructible<T>::value, - "Not compliant with std::is_trivially_destructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_destructible<T>::value, - "Not compliant with std::is_trivially_destructible; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE -}; - // is_trivially_default_constructible() -// -// Determines whether the passed type `T` is trivially default constructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_default_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: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop] -// "The predicate condition for a template specialization is_constructible<T, -// Args...> shall be satisfied if and only if the following variable -// definition would be well-formed for some invented variable t: -// -// T t(declval<Args>()...); -// -// is_trivially_constructible<T, Args...> additionally requires that the -// variable definition does not call any operation that is not trivial. -// For the purposes of this check, the call to std::declval is considered -// trivial." -// -// Notes from https://en.cppreference.com/w/cpp/types/is_constructible: -// In many implementations, is_nothrow_constructible also checks if the -// destructor throws because it is effectively noexcept(T(arg)). Same -// applies to is_trivially_constructible, which, in these implementations, also -// requires that the destructor is trivial. -// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 -// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116. -// -// "T obj();" need 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_default_constructible -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) - : std::is_trivially_default_constructible<T> { -#else - : std::integral_constant<bool, __has_trivial_constructor(T) && - std::is_default_constructible<T>::value && - is_trivially_destructible<T>::value> { -#endif -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - private: - static constexpr bool compliant = - std::is_trivially_default_constructible<T>::value == - is_trivially_default_constructible::value; - static_assert(compliant || std::is_trivially_default_constructible<T>::value, - "Not compliant with std::is_trivially_default_constructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_default_constructible<T>::value, - "Not compliant with std::is_trivially_default_constructible; " - "Standard: true, Implementation: false"); -#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 -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) - : std::is_trivially_move_constructible<T> { -#else - : std::conditional< - std::is_object<T>::value && !std::is_array<T>::value, - type_traits_internal::IsTriviallyMoveConstructibleObject<T>, - std::is_reference<T>>::type::type { -#endif -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - 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. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copy_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<const 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_copy_constructible - : std::conditional< - std::is_object<T>::value && !std::is_array<T>::value, - type_traits_internal::IsTriviallyCopyConstructibleObject<T>, - std::is_lvalue_reference<T>>::type::type { -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - private: - static constexpr bool compliant = - std::is_trivially_copy_constructible<T>::value == - is_trivially_copy_constructible::value; - static_assert(compliant || std::is_trivially_copy_constructible<T>::value, - "Not compliant with std::is_trivially_copy_constructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_copy_constructible<T>::value, - "Not compliant with std::is_trivially_copy_constructible; " - "Standard: true, Implementation: false"); -#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<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. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copy_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&, const T&>`. -template <typename T> -struct is_trivially_copy_assignable -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE - : std::is_trivially_copy_assignable<T> { -#else - : std::integral_constant< - bool, __has_trivial_assign(typename std::remove_reference<T>::type) && - absl::is_copy_assignable<T>::value> { -#endif -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE - private: - static constexpr bool compliant = - std::is_trivially_copy_assignable<T>::value == - is_trivially_copy_assignable::value; - static_assert(compliant || std::is_trivially_copy_assignable<T>::value, - "Not compliant with std::is_trivially_copy_assignable; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_copy_assignable<T>::value, - "Not compliant with std::is_trivially_copy_assignable; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE -}; +// Historical note: Abseil once provided implementations of these type traits +// for platforms that lacked full support. New code should prefer to use the +// std variants. +// +// See the documentation for the STL <type_traits> header for more information: +// https://en.cppreference.com/w/cpp/header/type_traits +using std::is_copy_assignable; +using std::is_move_assignable; +using std::is_trivially_copy_assignable; +using std::is_trivially_copy_constructible; +using std::is_trivially_default_constructible; +using std::is_trivially_destructible; +using std::is_trivially_move_assignable; +using std::is_trivially_move_constructible; #if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L template <typename T> @@ -532,55 +230,6 @@ template <typename T> using remove_cvref_t = typename remove_cvref<T>::type; #endif -namespace type_traits_internal { -// is_trivially_copyable() -// -// Determines whether the passed type `T` is trivially copyable. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copyable()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition -// of TriviallyCopyable. -// -// NOTE: `is_trivially_copyable<T>::value` is `true` if all of T's copy/move -// constructors/assignment operators are trivial or deleted, T has at least -// one non-deleted copy/move constructor/assignment operator, and T is trivially -// destructible. Arrays of trivially copyable types are trivially copyable. -// -// We expose this metafunction only for internal use within absl. - -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE) -template <typename T> -struct is_trivially_copyable : std::is_trivially_copyable<T> {}; -#else -template <typename T> -class is_trivially_copyable_impl { - using ExtentsRemoved = typename std::remove_all_extents<T>::type; - static constexpr bool kIsCopyOrMoveConstructible = - std::is_copy_constructible<ExtentsRemoved>::value || - std::is_move_constructible<ExtentsRemoved>::value; - static constexpr bool kIsCopyOrMoveAssignable = - absl::is_copy_assignable<ExtentsRemoved>::value || - absl::is_move_assignable<ExtentsRemoved>::value; - - public: - static constexpr bool kValue = - (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) && - (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) && - (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) && - is_trivially_destructible<ExtentsRemoved>::value && - // We need to check for this explicitly because otherwise we'll say - // references are trivial copyable when compiled by MSVC. - !std::is_reference<ExtentsRemoved>::value; -}; - -template <typename T> -struct is_trivially_copyable - : std::integral_constant< - bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {}; -#endif -} // namespace type_traits_internal - // ----------------------------------------------------------------------------- // C++14 "_t" trait aliases // ----------------------------------------------------------------------------- @@ -630,6 +279,7 @@ using remove_extent_t = typename std::remove_extent<T>::type; template <typename T> using remove_all_extents_t = typename std::remove_all_extents<T>::type; +ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING namespace type_traits_internal { // This trick to retrieve a default alignment is necessary for our // implementation of aligned_storage_t to be consistent with any @@ -648,6 +298,7 @@ struct default_alignment_of_aligned_storage< template <size_t Len, size_t Align = type_traits_internal:: default_alignment_of_aligned_storage<Len>::value> using aligned_storage_t = typename std::aligned_storage<Len, Align>::type; +ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING template <typename T> using decay_t = typename std::decay<T>::type; @@ -818,9 +469,14 @@ using swap_internal::StdSwapIsUnconstrained; } // namespace type_traits_internal // absl::is_trivially_relocatable<T> -// Detects whether a type is "trivially relocatable" -- meaning it can be -// relocated without invoking the constructor/destructor, using a form of move -// elision. +// +// Detects whether a type is known to be "trivially relocatable" -- meaning it +// can be relocated without invoking the constructor/destructor, using a form of +// move elision. +// +// This trait is conservative, for backwards compatibility. If it's true then +// the type is definitely trivially relocatable, but if it's false then the type +// may or may not be. // // Example: // @@ -834,14 +490,33 @@ using swap_internal::StdSwapIsUnconstrained; // Upstream documentation: // // https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable + +// If the compiler offers a builtin that tells us the answer, we can use that. +// This covers all of the cases in the fallback below, plus types that opt in +// using e.g. [[clang::trivial_abi]]. // -#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) +// Clang on Windows has the builtin, but it falsely claims types with a +// user-provided destructor are trivial (http://b/275003464). So we opt out +// there. +// +// TODO(b/275003464): remove the opt-out once the bug is fixed. +// +// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not +// work with NVCC either. +#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \ + !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \ + !defined(__NVCC__) template <class T> struct is_trivially_relocatable : std::integral_constant<bool, __is_trivially_relocatable(T)> {}; #else +// Otherwise we use a fallback that detects only those types we can feasibly +// detect. Any time that has trivial move-construction and destruction +// operations is by definition trivially relocatable. template <class T> -struct is_trivially_relocatable : std::integral_constant<bool, false> {}; +struct is_trivially_relocatable + : absl::conjunction<absl::is_trivially_move_constructible<T>, + absl::is_trivially_destructible<T>> {}; #endif // absl::is_constant_evaluated() |