1//
2// Copyright 2017 The Abseil Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16// -----------------------------------------------------------------------------
17// type_traits.h
18// -----------------------------------------------------------------------------
19//
20// This file contains C++11-compatible versions of standard <type_traits> API
21// functions for determining the characteristics of types. Such traits can
22// support type inference, classification, and transformation, as well as
23// make it easier to write templates based on generic type behavior.
24//
25// See https://en.cppreference.com/w/cpp/header/type_traits
26//
27// WARNING: use of many of the constructs in this header will count as "complex
28// template metaprogramming", so before proceeding, please carefully consider
29// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming
30//
31// WARNING: using template metaprogramming to detect or depend on API
32// features is brittle and not guaranteed. Neither the standard library nor
33// Abseil provides any guarantee that APIs are stable in the face of template
34// metaprogramming. Use with caution.
35#ifndef ABSL_META_TYPE_TRAITS_H_
36#define ABSL_META_TYPE_TRAITS_H_
37
38#include <cstddef>
39#include <functional>
40#include <string>
41#include <type_traits>
42#include <vector>
43
44#include "absl/base/attributes.h"
45#include "absl/base/config.h"
46
47#ifdef __cpp_lib_span
48#include <span> // NOLINT(build/c++20)
49#endif
50
51#ifdef ABSL_HAVE_STD_STRING_VIEW
52#include <string_view>
53#endif
54
55// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17
56// feature.
57#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
58#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__
59#else // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
60#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t)
61#endif // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
62
63namespace absl {
64ABSL_NAMESPACE_BEGIN
65
66namespace type_traits_internal {
67
68template <typename... Ts>
69struct VoidTImpl {
70 using type = void;
71};
72
73////////////////////////////////
74// Library Fundamentals V2 TS //
75////////////////////////////////
76
77// NOTE: The `is_detected` family of templates here differ from the library
78// fundamentals specification in that for library fundamentals, `Op<Args...>` is
79// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
80// substitution, regardless of whether or not the `::value` is accessed. That
81// is inconsistent with all other standard traits and prevents lazy evaluation
82// in larger contexts (such as if the `is_detected` check is a trailing argument
83// of a `conjunction`. This implementation opts to instead be lazy in the same
84// way that the standard traits are (this "defect" of the detection idiom
85// specifications has been reported).
86
87template <class Enabler, template <class...> class Op, class... Args>
88struct is_detected_impl {
89 using type = std::false_type;
90};
91
92template <template <class...> class Op, class... Args>
93struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
94 using type = std::true_type;
95};
96
97template <template <class...> class Op, class... Args>
98struct is_detected : is_detected_impl<void, Op, Args...>::type {};
99
100template <class Enabler, class To, template <class...> class Op, class... Args>
101struct is_detected_convertible_impl {
102 using type = std::false_type;
103};
104
105template <class To, template <class...> class Op, class... Args>
106struct is_detected_convertible_impl<
107 typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
108 To, Op, Args...> {
109 using type = std::true_type;
110};
111
112template <class To, template <class...> class Op, class... Args>
113struct is_detected_convertible
114 : is_detected_convertible_impl<void, To, Op, Args...>::type {};
115
116} // namespace type_traits_internal
117
118// void_t()
119//
120// Ignores the type of any its arguments and returns `void`. In general, this
121// metafunction allows you to create a general case that maps to `void` while
122// allowing specializations that map to specific types.
123//
124// This metafunction is designed to be a drop-in replacement for the C++17
125// `std::void_t` metafunction.
126//
127// NOTE: `absl::void_t` does not use the standard-specified implementation so
128// that it can remain compatible with gcc < 5.1. This can introduce slightly
129// different behavior, such as when ordering partial specializations.
130template <typename... Ts>
131using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type;
132
133// conjunction
134//
135// Performs a compile-time logical AND operation on the passed types (which
136// must have `::value` members convertible to `bool`. Short-circuits if it
137// encounters any `false` members (and does not compare the `::value` members
138// of any remaining arguments).
139//
140// This metafunction is designed to be a drop-in replacement for the C++17
141// `std::conjunction` metafunction.
142template <typename... Ts>
143struct conjunction : std::true_type {};
144
145template <typename T, typename... Ts>
146struct conjunction<T, Ts...>
147 : std::conditional<T::value, conjunction<Ts...>, T>::type {};
148
149template <typename T>
150struct conjunction<T> : T {};
151
152// disjunction
153//
154// Performs a compile-time logical OR operation on the passed types (which
155// must have `::value` members convertible to `bool`. Short-circuits if it
156// encounters any `true` members (and does not compare the `::value` members
157// of any remaining arguments).
158//
159// This metafunction is designed to be a drop-in replacement for the C++17
160// `std::disjunction` metafunction.
161template <typename... Ts>
162struct disjunction : std::false_type {};
163
164template <typename T, typename... Ts>
165struct disjunction<T, Ts...>
166 : std::conditional<T::value, T, disjunction<Ts...>>::type {};
167
168template <typename T>
169struct disjunction<T> : T {};
170
171// negation
172//
173// Performs a compile-time logical NOT operation on the passed type (which
174// must have `::value` members convertible to `bool`.
175//
176// This metafunction is designed to be a drop-in replacement for the C++17
177// `std::negation` metafunction.
178template <typename T>
179struct negation : std::integral_constant<bool, !T::value> {};
180
181// is_function()
182//
183// Determines whether the passed type `T` is a function type.
184//
185// This metafunction is designed to be a drop-in replacement for the C++11
186// `std::is_function()` metafunction for platforms that have incomplete C++11
187// support (such as libstdc++ 4.x).
188//
189// This metafunction works because appending `const` to a type does nothing to
190// function types and reference types (and forms a const-qualified type
191// otherwise).
192template <typename T>
193struct is_function
194 : std::integral_constant<
195 bool, !(std::is_reference<T>::value ||
196 std::is_const<typename std::add_const<T>::type>::value)> {};
197
198// is_copy_assignable()
199// is_move_assignable()
200// is_trivially_destructible()
201// is_trivially_default_constructible()
202// is_trivially_move_constructible()
203// is_trivially_copy_constructible()
204// is_trivially_move_assignable()
205// is_trivially_copy_assignable()
206//
207// Historical note: Abseil once provided implementations of these type traits
208// for platforms that lacked full support. New code should prefer to use the
209// std variants.
210//
211// See the documentation for the STL <type_traits> header for more information:
212// https://en.cppreference.com/w/cpp/header/type_traits
213using std::is_copy_assignable;
214using std::is_move_assignable;
215using std::is_trivially_copy_assignable;
216using std::is_trivially_copy_constructible;
217using std::is_trivially_default_constructible;
218using std::is_trivially_destructible;
219using std::is_trivially_move_assignable;
220using std::is_trivially_move_constructible;
221
222#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
223template <typename T>
224using remove_cvref = std::remove_cvref<T>;
225
226template <typename T>
227using remove_cvref_t = typename std::remove_cvref<T>::type;
228#else
229// remove_cvref()
230//
231// C++11 compatible implementation of std::remove_cvref which was added in
232// C++20.
233template <typename T>
234struct remove_cvref {
235 using type =
236 typename std::remove_cv<typename std::remove_reference<T>::type>::type;
237};
238
239template <typename T>
240using remove_cvref_t = typename remove_cvref<T>::type;
241#endif
242
243// -----------------------------------------------------------------------------
244// C++14 "_t" trait aliases
245// -----------------------------------------------------------------------------
246
247template <typename T>
248using remove_cv_t = typename std::remove_cv<T>::type;
249
250template <typename T>
251using remove_const_t = typename std::remove_const<T>::type;
252
253template <typename T>
254using remove_volatile_t = typename std::remove_volatile<T>::type;
255
256template <typename T>
257using add_cv_t = typename std::add_cv<T>::type;
258
259template <typename T>
260using add_const_t = typename std::add_const<T>::type;
261
262template <typename T>
263using add_volatile_t = typename std::add_volatile<T>::type;
264
265template <typename T>
266using remove_reference_t = typename std::remove_reference<T>::type;
267
268template <typename T>
269using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
270
271template <typename T>
272using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
273
274template <typename T>
275using remove_pointer_t = typename std::remove_pointer<T>::type;
276
277template <typename T>
278using add_pointer_t = typename std::add_pointer<T>::type;
279
280template <typename T>
281using make_signed_t = typename std::make_signed<T>::type;
282
283template <typename T>
284using make_unsigned_t = typename std::make_unsigned<T>::type;
285
286template <typename T>
287using remove_extent_t = typename std::remove_extent<T>::type;
288
289template <typename T>
290using remove_all_extents_t = typename std::remove_all_extents<T>::type;
291
292template <typename T>
293using decay_t = typename std::decay<T>::type;
294
295template <bool B, typename T = void>
296using enable_if_t = typename std::enable_if<B, T>::type;
297
298template <bool B, typename T, typename F>
299using conditional_t = typename std::conditional<B, T, F>::type;
300
301template <typename... T>
302using common_type_t = typename std::common_type<T...>::type;
303
304template <typename T>
305using underlying_type_t = typename std::underlying_type<T>::type;
306
307namespace type_traits_internal {
308
309#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
310 (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
311// std::result_of is deprecated (C++17) or removed (C++20)
312template <typename>
313struct result_of;
314template <typename F, typename... Args>
315struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
316#else
317template <typename F>
318using result_of = std::result_of<F>;
319#endif
320
321} // namespace type_traits_internal
322
323template <typename F>
324using result_of_t = typename type_traits_internal::result_of<F>::type;
325
326namespace type_traits_internal {
327// In MSVC we can't probe std::hash or stdext::hash because it triggers a
328// static_assert instead of failing substitution. Libc++ prior to 4.0
329// also used a static_assert.
330//
331#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
332 _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
333#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 0
334#else
335#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 1
336#endif
337
338#if !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
339template <typename Key, typename = size_t>
340struct IsHashable : std::true_type {};
341#else // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
342template <typename Key, typename = void>
343struct IsHashable : std::false_type {};
344
345template <typename Key>
346struct IsHashable<
347 Key,
348 absl::enable_if_t<std::is_convertible<
349 decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())),
350 std::size_t>::value>> : std::true_type {};
351#endif // !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
352
353struct AssertHashEnabledHelper {
354 private:
355 static void Sink(...) {}
356 struct NAT {};
357
358 template <class Key>
359 static auto GetReturnType(int)
360 -> decltype(std::declval<std::hash<Key>>()(std::declval<Key const&>()));
361 template <class Key>
362 static NAT GetReturnType(...);
363
364 template <class Key>
365 static std::nullptr_t DoIt() {
366 static_assert(IsHashable<Key>::value,
367 "std::hash<Key> does not provide a call operator");
368 static_assert(
369 std::is_default_constructible<std::hash<Key>>::value,
370 "std::hash<Key> must be default constructible when it is enabled");
371 static_assert(
372 std::is_copy_constructible<std::hash<Key>>::value,
373 "std::hash<Key> must be copy constructible when it is enabled");
374 static_assert(absl::is_copy_assignable<std::hash<Key>>::value,
375 "std::hash<Key> must be copy assignable when it is enabled");
376 // is_destructible is unchecked as it's implied by each of the
377 // is_constructible checks.
378 using ReturnType = decltype(GetReturnType<Key>(0));
379 static_assert(std::is_same<ReturnType, NAT>::value ||
380 std::is_same<ReturnType, size_t>::value,
381 "std::hash<Key> must return size_t");
382 return nullptr;
383 }
384
385 template <class... Ts>
386 friend void AssertHashEnabled();
387};
388
389template <class... Ts>
390inline void AssertHashEnabled() {
391 using Helper = AssertHashEnabledHelper;
392 Helper::Sink(Helper::DoIt<Ts>()...);
393}
394
395} // namespace type_traits_internal
396
397// An internal namespace that is required to implement the C++17 swap traits.
398// It is not further nested in type_traits_internal to avoid long symbol names.
399namespace swap_internal {
400
401// Necessary for the traits.
402using std::swap;
403
404// This declaration prevents global `swap` and `absl::swap` overloads from being
405// considered unless ADL picks them up.
406void swap();
407
408template <class T>
409using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>()));
410
411// NOTE: This dance with the default template parameter is for MSVC.
412template <class T,
413 class IsNoexcept = std::integral_constant<
414 bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>>
415using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type;
416
417// IsSwappable
418//
419// Determines whether the standard swap idiom is a valid expression for
420// arguments of type `T`.
421template <class T>
422struct IsSwappable
423 : absl::type_traits_internal::is_detected<IsSwappableImpl, T> {};
424
425// IsNothrowSwappable
426//
427// Determines whether the standard swap idiom is a valid expression for
428// arguments of type `T` and is noexcept.
429template <class T>
430struct IsNothrowSwappable
431 : absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {};
432
433// Swap()
434//
435// Performs the swap idiom from a namespace where valid candidates may only be
436// found in `std` or via ADL.
437template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0>
438void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) {
439 swap(lhs, rhs);
440}
441
442// StdSwapIsUnconstrained
443//
444// Some standard library implementations are broken in that they do not
445// constrain `std::swap`. This will effectively tell us if we are dealing with
446// one of those implementations.
447using StdSwapIsUnconstrained = IsSwappable<void()>;
448
449} // namespace swap_internal
450
451namespace type_traits_internal {
452
453// Make the swap-related traits/function accessible from this namespace.
454using swap_internal::IsNothrowSwappable;
455using swap_internal::IsSwappable;
456using swap_internal::StdSwapIsUnconstrained;
457using swap_internal::Swap;
458
459} // namespace type_traits_internal
460
461// absl::is_trivially_relocatable<T>
462//
463// Detects whether a type is known to be "trivially relocatable" -- meaning it
464// can be relocated from one place to another as if by memcpy/memmove.
465// This implies that its object representation doesn't depend on its address,
466// and also none of its special member functions do anything strange.
467//
468// This trait is conservative. If it's true then the type is definitely
469// trivially relocatable, but if it's false then the type may or may not be. For
470// example, std::vector<int> is trivially relocatable on every known STL
471// implementation, but absl::is_trivially_relocatable<std::vector<int>> remains
472// false.
473//
474// Example:
475//
476// if constexpr (absl::is_trivially_relocatable<T>::value) {
477// memcpy(new_location, old_location, sizeof(T));
478// } else {
479// new(new_location) T(std::move(*old_location));
480// old_location->~T();
481// }
482//
483// Upstream documentation:
484//
485// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
486
487// If the compiler offers a builtin that tells us the answer, we can use that.
488// This covers all of the cases in the fallback below, plus types that opt in
489// using e.g. [[clang::trivial_abi]].
490//
491// Clang on Windows has the builtin, but it falsely claims types with a
492// user-provided destructor are trivial (http://b/275003464). So we opt out
493// there.
494//
495// TODO(b/275003464): remove the opt-out once the bug is fixed.
496//
497// Starting with Xcode 15, the Apple compiler will falsely say a type
498// with a user-provided move constructor is trivially relocatable
499// (b/324278148). We will opt out without a version check, due to
500// the fluidity of Apple versions.
501//
502// TODO(b/324278148): If all versions we use have the bug fixed, then
503// remove the condition.
504//
505// Clang on all platforms fails to detect that a type with a user-provided
506// move-assignment operator is not trivially relocatable so we also check for
507// is_trivially_move_assignable for Clang.
508//
509// TODO(b/325479096): Remove the Clang is_trivially_move_assignable version once
510// Clang's behavior is fixed.
511//
512// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not
513// work with NVCC either.
514#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
515 (defined(__cpp_impl_trivially_relocatable) || \
516 (!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
517template <class T>
518struct is_trivially_relocatable
519 : std::integral_constant<bool, __is_trivially_relocatable(T)> {};
520#elif ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && defined(__clang__) && \
521 !(defined(_WIN32) || defined(_WIN64)) && !defined(__APPLE__) && \
522 !defined(__NVCC__)
523template <class T>
524struct is_trivially_relocatable
525 : std::integral_constant<
526 bool, std::is_trivially_copyable<T>::value ||
527 (__is_trivially_relocatable(T) &&
528 std::is_trivially_move_assignable<T>::value)> {};
529#else
530// Otherwise we use a fallback that detects only those types we can feasibly
531// detect. Any type that is trivially copyable is by definition trivially
532// relocatable.
533template <class T>
534struct is_trivially_relocatable : std::is_trivially_copyable<T> {};
535#endif
536
537// absl::is_constant_evaluated()
538//
539// Detects whether the function call occurs within a constant-evaluated context.
540// Returns true if the evaluation of the call occurs within the evaluation of an
541// expression or conversion that is manifestly constant-evaluated; otherwise
542// returns false.
543//
544// This function is implemented in terms of `std::is_constant_evaluated` for
545// c++20 and up. For older c++ versions, the function is implemented in terms
546// of `__builtin_is_constant_evaluated` if available, otherwise the function
547// will fail to compile.
548//
549// Applications can inspect `ABSL_HAVE_CONSTANT_EVALUATED` at compile time
550// to check if this function is supported.
551//
552// Example:
553//
554// constexpr MyClass::MyClass(int param) {
555// #ifdef ABSL_HAVE_CONSTANT_EVALUATED
556// if (!absl::is_constant_evaluated()) {
557// ABSL_LOG(INFO) << "MyClass(" << param << ")";
558// }
559// #endif // ABSL_HAVE_CONSTANT_EVALUATED
560// }
561//
562// Upstream documentation:
563//
564// http://en.cppreference.com/w/cpp/types/is_constant_evaluated
565// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated
566//
567#if defined(ABSL_HAVE_CONSTANT_EVALUATED)
568constexpr bool is_constant_evaluated() noexcept {
569#ifdef __cpp_lib_is_constant_evaluated
570 return std::is_constant_evaluated();
571#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
572 return __builtin_is_constant_evaluated();
573#endif
574}
575#endif // ABSL_HAVE_CONSTANT_EVALUATED
576
577namespace type_traits_internal {
578
579// Detects if a class's definition has declared itself to be an owner by
580// declaring
581// using absl_internal_is_view = std::true_type;
582// as a member.
583// Types that don't want either must either omit this declaration entirely, or
584// (if e.g. inheriting from a base class) define the member to something that
585// isn't a Boolean trait class, such as `void`.
586// Do not specialize or use this directly. It's an implementation detail.
587template <typename T, typename = void>
588struct IsOwnerImpl : std::false_type {
589 static_assert(std::is_same<T, absl::remove_cvref_t<T>>::value,
590 "type must lack qualifiers");
591};
592
593template <typename T>
594struct IsOwnerImpl<
595 T,
596 std::enable_if_t<std::is_class<typename T::absl_internal_is_view>::value>>
597 : absl::negation<typename T::absl_internal_is_view> {};
598
599// A trait to determine whether a type is an owner.
600// Do *not* depend on the correctness of this trait for correct code behavior.
601// It is only a safety feature and its value may change in the future.
602// Do not specialize this; instead, define the member trait inside your type so
603// that it can be auto-detected, and to prevent ODR violations.
604// If it ever becomes possible to detect [[gsl::Owner]], we should leverage it:
605// https://wg21.link/p1179
606template <typename T>
607struct IsOwner : IsOwnerImpl<T> {};
608
609template <typename T, typename Traits, typename Alloc>
610struct IsOwner<std::basic_string<T, Traits, Alloc>> : std::true_type {};
611
612template <typename T, typename Alloc>
613struct IsOwner<std::vector<T, Alloc>> : std::true_type {};
614
615// Detects if a class's definition has declared itself to be a view by declaring
616// using absl_internal_is_view = std::true_type;
617// as a member.
618// Do not specialize or use this directly.
619template <typename T, typename = void>
620struct IsViewImpl : std::false_type {
621 static_assert(std::is_same<T, absl::remove_cvref_t<T>>::value,
622 "type must lack qualifiers");
623};
624
625template <typename T>
626struct IsViewImpl<
627 T,
628 std::enable_if_t<std::is_class<typename T::absl_internal_is_view>::value>>
629 : T::absl_internal_is_view {};
630
631// A trait to determine whether a type is a view.
632// Do *not* depend on the correctness of this trait for correct code behavior.
633// It is only a safety feature, and its value may change in the future.
634// Do not specialize this trait. Instead, define the member
635// using absl_internal_is_view = std::true_type;
636// in your class to allow its detection while preventing ODR violations.
637// If it ever becomes possible to detect [[gsl::Pointer]], we should leverage
638// it: https://wg21.link/p1179
639template <typename T>
640struct IsView : std::integral_constant<bool, std::is_pointer<T>::value ||
641 IsViewImpl<T>::value> {};
642
643#ifdef ABSL_HAVE_STD_STRING_VIEW
644template <typename Char, typename Traits>
645struct IsView<std::basic_string_view<Char, Traits>> : std::true_type {};
646#endif
647
648#ifdef __cpp_lib_span
649template <typename T>
650struct IsView<std::span<T>> : std::true_type {};
651#endif
652
653// Determines whether the assignment of the given types is lifetime-bound.
654// Do *not* depend on the correctness of this trait for correct code behavior.
655// It is only a safety feature and its value may change in the future.
656// If it ever becomes possible to detect [[clang::lifetimebound]] directly,
657// we should change the implementation to leverage that.
658// Until then, we consider an assignment from an "owner" (such as std::string)
659// to a "view" (such as std::string_view) to be a lifetime-bound assignment.
660template <typename T, typename U>
661using IsLifetimeBoundAssignment = absl::conjunction<
662 std::integral_constant<bool, !std::is_lvalue_reference<U>::value>,
663 IsOwner<absl::remove_cvref_t<U>>, IsView<absl::remove_cvref_t<T>>>;
664
665} // namespace type_traits_internal
666
667ABSL_NAMESPACE_END
668} // namespace absl
669
670#endif // ABSL_META_TYPE_TRAITS_H_
671