Commit 4eef1617 by Derek Mauro Committed by Copybara-Service

Update absl::allocator_traits and absl::pointer_traits to always

use std::allocator_traits and std::pointer traits.

Note that the reason given in the comments for these implementations was
incorrect. Both of these exist in C++11, but not all compilers had
working implementations, so Abseil backfiled them. All supported compilers
now have working implementations.

https://en.cppreference.com/w/cpp/memory/allocator_traits
https://en.cppreference.com/w/cpp/memory/pointer_traits

Documentation has been updated to recommend the std:: spellings.

Fixes #1366

PiperOrigin-RevId: 503532746
Change-Id: Ia437f65a4c752581195dc582a41831b479d096c6
parent 52495da0
......@@ -158,6 +158,26 @@ std::weak_ptr<T> WeakenPtr(const std::shared_ptr<T>& ptr) {
return std::weak_ptr<T>(ptr);
}
// -----------------------------------------------------------------------------
// Class Template: pointer_traits
// -----------------------------------------------------------------------------
//
// Historical note: Abseil once provided an implementation of
// `std::pointer_traits` for platforms that had not yet provided it. Those
// platforms are no longer supported. New code should simply use
// `std::pointer_traits`.
using std::pointer_traits;
// -----------------------------------------------------------------------------
// Class Template: allocator_traits
// -----------------------------------------------------------------------------
//
// Historical note: Abseil once provided an implementation of
// `std::allocator_traits` for platforms that had not yet provided it. Those
// platforms are no longer supported. New code should simply use
// `std::allocator_traits`.
using std::allocator_traits;
namespace memory_internal {
// ExtractOr<E, O, D>::type evaluates to E<O> if possible. Otherwise, D.
......@@ -175,357 +195,6 @@ struct ExtractOr<Extract, Obj, Default, void_t<Extract<Obj>>> {
template <template <typename> class Extract, typename Obj, typename Default>
using ExtractOrT = typename ExtractOr<Extract, Obj, Default, void>::type;
// Extractors for the features of allocators.
template <typename T>
using GetPointer = typename T::pointer;
template <typename T>
using GetConstPointer = typename T::const_pointer;
template <typename T>
using GetVoidPointer = typename T::void_pointer;
template <typename T>
using GetConstVoidPointer = typename T::const_void_pointer;
template <typename T>
using GetDifferenceType = typename T::difference_type;
template <typename T>
using GetSizeType = typename T::size_type;
template <typename T>
using GetPropagateOnContainerCopyAssignment =
typename T::propagate_on_container_copy_assignment;
template <typename T>
using GetPropagateOnContainerMoveAssignment =
typename T::propagate_on_container_move_assignment;
template <typename T>
using GetPropagateOnContainerSwap = typename T::propagate_on_container_swap;
template <typename T>
using GetIsAlwaysEqual = typename T::is_always_equal;
template <typename T>
struct GetFirstArg;
template <template <typename...> class Class, typename T, typename... Args>
struct GetFirstArg<Class<T, Args...>> {
using type = T;
};
template <typename Ptr, typename = void>
struct ElementType {
using type = typename GetFirstArg<Ptr>::type;
};
template <typename T>
struct ElementType<T, void_t<typename T::element_type>> {
using type = typename T::element_type;
};
template <typename T, typename U>
struct RebindFirstArg;
template <template <typename...> class Class, typename T, typename... Args,
typename U>
struct RebindFirstArg<Class<T, Args...>, U> {
using type = Class<U, Args...>;
};
template <typename T, typename U, typename = void>
struct RebindPtr {
using type = typename RebindFirstArg<T, U>::type;
};
template <typename T, typename U>
struct RebindPtr<T, U, void_t<typename T::template rebind<U>>> {
using type = typename T::template rebind<U>;
};
template <typename T, typename U>
constexpr bool HasRebindAlloc(...) {
return false;
}
template <typename T, typename U>
constexpr bool HasRebindAlloc(typename T::template rebind<U>::other*) {
return true;
}
template <typename T, typename U, bool = HasRebindAlloc<T, U>(nullptr)>
struct RebindAlloc {
using type = typename RebindFirstArg<T, U>::type;
};
template <typename T, typename U>
struct RebindAlloc<T, U, true> {
using type = typename T::template rebind<U>::other;
};
} // namespace memory_internal
// -----------------------------------------------------------------------------
// Class Template: pointer_traits
// -----------------------------------------------------------------------------
//
// An implementation of C++11's std::pointer_traits.
//
// Provided for portability on toolchains that have a working C++11 compiler,
// but the standard library is lacking in C++11 support. For example, some
// version of the Android NDK.
//
template <typename Ptr>
struct pointer_traits {
using pointer = Ptr;
// element_type:
// Ptr::element_type if present. Otherwise T if Ptr is a template
// instantiation Template<T, Args...>
using element_type = typename memory_internal::ElementType<Ptr>::type;
// difference_type:
// Ptr::difference_type if present, otherwise std::ptrdiff_t
using difference_type =
memory_internal::ExtractOrT<memory_internal::GetDifferenceType, Ptr,
std::ptrdiff_t>;
// rebind:
// Ptr::rebind<U> if exists, otherwise Template<U, Args...> if Ptr is a
// template instantiation Template<T, Args...>
template <typename U>
using rebind = typename memory_internal::RebindPtr<Ptr, U>::type;
// pointer_to:
// Calls Ptr::pointer_to(r)
static pointer pointer_to(element_type& r) { // NOLINT(runtime/references)
return Ptr::pointer_to(r);
}
};
// Specialization for T*.
template <typename T>
struct pointer_traits<T*> {
using pointer = T*;
using element_type = T;
using difference_type = std::ptrdiff_t;
template <typename U>
using rebind = U*;
// pointer_to:
// Calls std::addressof(r)
static pointer pointer_to(
element_type& r) noexcept { // NOLINT(runtime/references)
return std::addressof(r);
}
};
// -----------------------------------------------------------------------------
// Class Template: allocator_traits
// -----------------------------------------------------------------------------
//
// A C++11 compatible implementation of C++17's std::allocator_traits.
//
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
using std::allocator_traits;
#else // __cplusplus >= 201703L
template <typename Alloc>
struct allocator_traits {
using allocator_type = Alloc;
// value_type:
// Alloc::value_type
using value_type = typename Alloc::value_type;
// pointer:
// Alloc::pointer if present, otherwise value_type*
using pointer = memory_internal::ExtractOrT<memory_internal::GetPointer,
Alloc, value_type*>;
// const_pointer:
// Alloc::const_pointer if present, otherwise
// absl::pointer_traits<pointer>::rebind<const value_type>
using const_pointer =
memory_internal::ExtractOrT<memory_internal::GetConstPointer, Alloc,
typename absl::pointer_traits<pointer>::
template rebind<const value_type>>;
// void_pointer:
// Alloc::void_pointer if present, otherwise
// absl::pointer_traits<pointer>::rebind<void>
using void_pointer = memory_internal::ExtractOrT<
memory_internal::GetVoidPointer, Alloc,
typename absl::pointer_traits<pointer>::template rebind<void>>;
// const_void_pointer:
// Alloc::const_void_pointer if present, otherwise
// absl::pointer_traits<pointer>::rebind<const void>
using const_void_pointer = memory_internal::ExtractOrT<
memory_internal::GetConstVoidPointer, Alloc,
typename absl::pointer_traits<pointer>::template rebind<const void>>;
// difference_type:
// Alloc::difference_type if present, otherwise
// absl::pointer_traits<pointer>::difference_type
using difference_type = memory_internal::ExtractOrT<
memory_internal::GetDifferenceType, Alloc,
typename absl::pointer_traits<pointer>::difference_type>;
// size_type:
// Alloc::size_type if present, otherwise
// std::make_unsigned<difference_type>::type
using size_type = memory_internal::ExtractOrT<
memory_internal::GetSizeType, Alloc,
typename std::make_unsigned<difference_type>::type>;
// propagate_on_container_copy_assignment:
// Alloc::propagate_on_container_copy_assignment if present, otherwise
// std::false_type
using propagate_on_container_copy_assignment = memory_internal::ExtractOrT<
memory_internal::GetPropagateOnContainerCopyAssignment, Alloc,
std::false_type>;
// propagate_on_container_move_assignment:
// Alloc::propagate_on_container_move_assignment if present, otherwise
// std::false_type
using propagate_on_container_move_assignment = memory_internal::ExtractOrT<
memory_internal::GetPropagateOnContainerMoveAssignment, Alloc,
std::false_type>;
// propagate_on_container_swap:
// Alloc::propagate_on_container_swap if present, otherwise std::false_type
using propagate_on_container_swap =
memory_internal::ExtractOrT<memory_internal::GetPropagateOnContainerSwap,
Alloc, std::false_type>;
// is_always_equal:
// Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
using is_always_equal =
memory_internal::ExtractOrT<memory_internal::GetIsAlwaysEqual, Alloc,
typename std::is_empty<Alloc>::type>;
// rebind_alloc:
// Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc
// is Alloc<U, Args>
template <typename T>
using rebind_alloc = typename memory_internal::RebindAlloc<Alloc, T>::type;
// rebind_traits:
// absl::allocator_traits<rebind_alloc<T>>
template <typename T>
using rebind_traits = absl::allocator_traits<rebind_alloc<T>>;
// allocate(Alloc& a, size_type n):
// Calls a.allocate(n)
static pointer allocate(Alloc& a, // NOLINT(runtime/references)
size_type n) {
return a.allocate(n);
}
// allocate(Alloc& a, size_type n, const_void_pointer hint):
// Calls a.allocate(n, hint) if possible.
// If not possible, calls a.allocate(n)
static pointer allocate(Alloc& a, size_type n, // NOLINT(runtime/references)
const_void_pointer hint) {
return allocate_impl(0, a, n, hint);
}
// deallocate(Alloc& a, pointer p, size_type n):
// Calls a.deallocate(p, n)
static void deallocate(Alloc& a, pointer p, // NOLINT(runtime/references)
size_type n) {
a.deallocate(p, n);
}
// construct(Alloc& a, T* p, Args&&... args):
// Calls a.construct(p, std::forward<Args>(args)...) if possible.
// If not possible, calls
// ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
template <typename T, typename... Args>
static void construct(Alloc& a, T* p, // NOLINT(runtime/references)
Args&&... args) {
construct_impl(0, a, p, std::forward<Args>(args)...);
}
// destroy(Alloc& a, T* p):
// Calls a.destroy(p) if possible. If not possible, calls p->~T().
template <typename T>
static void destroy(Alloc& a, T* p) { // NOLINT(runtime/references)
destroy_impl(0, a, p);
}
// max_size(const Alloc& a):
// Returns a.max_size() if possible. If not possible, returns
// std::numeric_limits<size_type>::max() / sizeof(value_type)
static size_type max_size(const Alloc& a) { return max_size_impl(0, a); }
// select_on_container_copy_construction(const Alloc& a):
// Returns a.select_on_container_copy_construction() if possible.
// If not possible, returns a.
static Alloc select_on_container_copy_construction(const Alloc& a) {
return select_on_container_copy_construction_impl(0, a);
}
private:
template <typename A>
static auto allocate_impl(int, A& a, // NOLINT(runtime/references)
size_type n, const_void_pointer hint)
-> decltype(a.allocate(n, hint)) {
return a.allocate(n, hint);
}
static pointer allocate_impl(char, Alloc& a, // NOLINT(runtime/references)
size_type n, const_void_pointer) {
return a.allocate(n);
}
template <typename A, typename... Args>
static auto construct_impl(int, A& a, // NOLINT(runtime/references)
Args&&... args)
-> decltype(a.construct(std::forward<Args>(args)...)) {
a.construct(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
static void construct_impl(char, Alloc&, T* p, Args&&... args) {
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
template <typename A, typename T>
static auto destroy_impl(int, A& a, // NOLINT(runtime/references)
T* p) -> decltype(a.destroy(p)) {
a.destroy(p);
}
template <typename T>
static void destroy_impl(char, Alloc&, T* p) {
p->~T();
}
template <typename A>
static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) {
return a.max_size();
}
static size_type max_size_impl(char, const Alloc&) {
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
}
template <typename A>
static auto select_on_container_copy_construction_impl(int, const A& a)
-> decltype(a.select_on_container_copy_construction()) {
return a.select_on_container_copy_construction();
}
static Alloc select_on_container_copy_construction_impl(char,
const Alloc& a) {
return a;
}
};
#endif // __cplusplus >= 201703L
namespace memory_internal {
// This template alias transforms Alloc::is_nothrow into a metafunction with
// Alloc as a parameter so it can be used with ExtractOrT<>.
template <typename Alloc>
......
......@@ -190,338 +190,6 @@ TEST(RawPtrTest, NotAPointer) {
}
*/
template <typename T>
struct SmartPointer {
using difference_type = char;
};
struct PointerWith {
using element_type = int32_t;
using difference_type = int16_t;
template <typename U>
using rebind = SmartPointer<U>;
static PointerWith pointer_to(
element_type& r) { // NOLINT(runtime/references)
return PointerWith{&r};
}
element_type* ptr;
};
template <typename... Args>
struct PointerWithout {};
TEST(PointerTraits, Types) {
using TraitsWith = absl::pointer_traits<PointerWith>;
EXPECT_TRUE((std::is_same<TraitsWith::pointer, PointerWith>::value));
EXPECT_TRUE((std::is_same<TraitsWith::element_type, int32_t>::value));
EXPECT_TRUE((std::is_same<TraitsWith::difference_type, int16_t>::value));
EXPECT_TRUE((
std::is_same<TraitsWith::rebind<int64_t>, SmartPointer<int64_t>>::value));
using TraitsWithout = absl::pointer_traits<PointerWithout<double, int>>;
EXPECT_TRUE((std::is_same<TraitsWithout::pointer,
PointerWithout<double, int>>::value));
EXPECT_TRUE((std::is_same<TraitsWithout::element_type, double>::value));
EXPECT_TRUE(
(std::is_same<TraitsWithout ::difference_type, std::ptrdiff_t>::value));
EXPECT_TRUE((std::is_same<TraitsWithout::rebind<int64_t>,
PointerWithout<int64_t, int>>::value));
using TraitsRawPtr = absl::pointer_traits<char*>;
EXPECT_TRUE((std::is_same<TraitsRawPtr::pointer, char*>::value));
EXPECT_TRUE((std::is_same<TraitsRawPtr::element_type, char>::value));
EXPECT_TRUE(
(std::is_same<TraitsRawPtr::difference_type, std::ptrdiff_t>::value));
EXPECT_TRUE((std::is_same<TraitsRawPtr::rebind<int64_t>, int64_t*>::value));
}
TEST(PointerTraits, Functions) {
int i;
EXPECT_EQ(&i, absl::pointer_traits<PointerWith>::pointer_to(i).ptr);
EXPECT_EQ(&i, absl::pointer_traits<int*>::pointer_to(i));
}
TEST(AllocatorTraits, Typedefs) {
struct A {
struct value_type {};
};
EXPECT_TRUE((
std::is_same<A,
typename absl::allocator_traits<A>::allocator_type>::value));
EXPECT_TRUE(
(std::is_same<A::value_type,
typename absl::allocator_traits<A>::value_type>::value));
struct X {};
struct HasPointer {
using value_type = X;
using pointer = SmartPointer<X>;
};
EXPECT_TRUE((std::is_same<SmartPointer<X>, typename absl::allocator_traits<
HasPointer>::pointer>::value));
EXPECT_TRUE(
(std::is_same<A::value_type*,
typename absl::allocator_traits<A>::pointer>::value));
EXPECT_TRUE(
(std::is_same<
SmartPointer<const X>,
typename absl::allocator_traits<HasPointer>::const_pointer>::value));
EXPECT_TRUE(
(std::is_same<const A::value_type*,
typename absl::allocator_traits<A>::const_pointer>::value));
struct HasVoidPointer {
using value_type = X;
struct void_pointer {};
};
EXPECT_TRUE((std::is_same<HasVoidPointer::void_pointer,
typename absl::allocator_traits<
HasVoidPointer>::void_pointer>::value));
EXPECT_TRUE(
(std::is_same<SmartPointer<void>, typename absl::allocator_traits<
HasPointer>::void_pointer>::value));
struct HasConstVoidPointer {
using value_type = X;
struct const_void_pointer {};
};
EXPECT_TRUE(
(std::is_same<HasConstVoidPointer::const_void_pointer,
typename absl::allocator_traits<
HasConstVoidPointer>::const_void_pointer>::value));
EXPECT_TRUE((std::is_same<SmartPointer<const void>,
typename absl::allocator_traits<
HasPointer>::const_void_pointer>::value));
struct HasDifferenceType {
using value_type = X;
using difference_type = int;
};
EXPECT_TRUE(
(std::is_same<int, typename absl::allocator_traits<
HasDifferenceType>::difference_type>::value));
EXPECT_TRUE((std::is_same<char, typename absl::allocator_traits<
HasPointer>::difference_type>::value));
struct HasSizeType {
using value_type = X;
using size_type = unsigned int;
};
EXPECT_TRUE((std::is_same<unsigned int, typename absl::allocator_traits<
HasSizeType>::size_type>::value));
EXPECT_TRUE((std::is_same<unsigned char, typename absl::allocator_traits<
HasPointer>::size_type>::value));
struct HasPropagateOnCopy {
using value_type = X;
struct propagate_on_container_copy_assignment {};
};
EXPECT_TRUE(
(std::is_same<HasPropagateOnCopy::propagate_on_container_copy_assignment,
typename absl::allocator_traits<HasPropagateOnCopy>::
propagate_on_container_copy_assignment>::value));
EXPECT_TRUE(
(std::is_same<std::false_type,
typename absl::allocator_traits<
A>::propagate_on_container_copy_assignment>::value));
struct HasPropagateOnMove {
using value_type = X;
struct propagate_on_container_move_assignment {};
};
EXPECT_TRUE(
(std::is_same<HasPropagateOnMove::propagate_on_container_move_assignment,
typename absl::allocator_traits<HasPropagateOnMove>::
propagate_on_container_move_assignment>::value));
EXPECT_TRUE(
(std::is_same<std::false_type,
typename absl::allocator_traits<
A>::propagate_on_container_move_assignment>::value));
struct HasPropagateOnSwap {
using value_type = X;
struct propagate_on_container_swap {};
};
EXPECT_TRUE(
(std::is_same<HasPropagateOnSwap::propagate_on_container_swap,
typename absl::allocator_traits<HasPropagateOnSwap>::
propagate_on_container_swap>::value));
EXPECT_TRUE(
(std::is_same<std::false_type, typename absl::allocator_traits<A>::
propagate_on_container_swap>::value));
struct HasIsAlwaysEqual {
using value_type = X;
struct is_always_equal {};
};
EXPECT_TRUE((std::is_same<HasIsAlwaysEqual::is_always_equal,
typename absl::allocator_traits<
HasIsAlwaysEqual>::is_always_equal>::value));
EXPECT_TRUE((std::is_same<std::true_type, typename absl::allocator_traits<
A>::is_always_equal>::value));
struct NonEmpty {
using value_type = X;
int i;
};
EXPECT_TRUE(
(std::is_same<std::false_type,
absl::allocator_traits<NonEmpty>::is_always_equal>::value));
}
template <typename T>
struct AllocWithPrivateInheritance : private std::allocator<T> {
using value_type = T;
};
TEST(AllocatorTraits, RebindWithPrivateInheritance) {
// Regression test for some versions of gcc that do not like the sfinae we
// used in combination with private inheritance.
EXPECT_TRUE(
(std::is_same<AllocWithPrivateInheritance<int>,
absl::allocator_traits<AllocWithPrivateInheritance<char>>::
rebind_alloc<int>>::value));
}
template <typename T>
struct Rebound {};
struct AllocWithRebind {
using value_type = int;
template <typename T>
struct rebind {
using other = Rebound<T>;
};
};
template <typename T, typename U>
struct AllocWithoutRebind {
using value_type = int;
};
TEST(AllocatorTraits, Rebind) {
EXPECT_TRUE(
(std::is_same<Rebound<int>,
typename absl::allocator_traits<
AllocWithRebind>::template rebind_alloc<int>>::value));
EXPECT_TRUE(
(std::is_same<absl::allocator_traits<Rebound<int>>,
typename absl::allocator_traits<
AllocWithRebind>::template rebind_traits<int>>::value));
EXPECT_TRUE(
(std::is_same<AllocWithoutRebind<double, char>,
typename absl::allocator_traits<AllocWithoutRebind<
int, char>>::template rebind_alloc<double>>::value));
EXPECT_TRUE(
(std::is_same<absl::allocator_traits<AllocWithoutRebind<double, char>>,
typename absl::allocator_traits<AllocWithoutRebind<
int, char>>::template rebind_traits<double>>::value));
}
struct TestValue {
TestValue() {}
explicit TestValue(int* trace) : trace(trace) { ++*trace; }
~TestValue() {
if (trace) --*trace;
}
int* trace = nullptr;
};
struct MinimalMockAllocator {
MinimalMockAllocator() : value(0) {}
explicit MinimalMockAllocator(int value) : value(value) {}
MinimalMockAllocator(const MinimalMockAllocator& other)
: value(other.value) {}
using value_type = TestValue;
MOCK_METHOD(value_type*, allocate, (size_t));
MOCK_METHOD(void, deallocate, (value_type*, size_t));
int value;
};
TEST(AllocatorTraits, FunctionsMinimal) {
int trace = 0;
int hint;
alignas(TestValue) char buffer[sizeof(TestValue)];
auto* x = reinterpret_cast<TestValue*>(buffer);
MinimalMockAllocator mock;
using Traits = absl::allocator_traits<MinimalMockAllocator>;
EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(x));
EXPECT_CALL(mock, deallocate(x, 7));
EXPECT_EQ(x, Traits::allocate(mock, 7));
static_cast<void>(Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
EXPECT_EQ(x, Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
Traits::deallocate(mock, x, 7);
EXPECT_EQ(0, trace);
Traits::construct(mock, x, &trace);
EXPECT_EQ(1, trace);
Traits::destroy(mock, x);
EXPECT_EQ(0, trace);
EXPECT_EQ(std::numeric_limits<size_t>::max() / sizeof(TestValue),
Traits::max_size(mock));
EXPECT_EQ(0, mock.value);
EXPECT_EQ(0, Traits::select_on_container_copy_construction(mock).value);
}
struct FullMockAllocator {
FullMockAllocator() : value(0) {}
explicit FullMockAllocator(int value) : value(value) {}
FullMockAllocator(const FullMockAllocator& other) : value(other.value) {}
using value_type = TestValue;
MOCK_METHOD(value_type*, allocate, (size_t));
MOCK_METHOD(value_type*, allocate, (size_t, const void*));
MOCK_METHOD(void, construct, (value_type*, int*));
MOCK_METHOD(void, destroy, (value_type*));
MOCK_METHOD(size_t, max_size, (),
(const));
MOCK_METHOD(FullMockAllocator, select_on_container_copy_construction, (),
(const));
int value;
};
TEST(AllocatorTraits, FunctionsFull) {
int trace = 0;
int hint;
TestValue x(&trace), y;
FullMockAllocator mock;
using Traits = absl::allocator_traits<FullMockAllocator>;
EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(&x));
EXPECT_CALL(mock, allocate(13, &hint)).WillRepeatedly(Return(&y));
EXPECT_CALL(mock, construct(&x, &trace));
EXPECT_CALL(mock, destroy(&x));
EXPECT_CALL(mock, max_size()).WillRepeatedly(Return(17u));
EXPECT_CALL(mock, select_on_container_copy_construction())
.WillRepeatedly(Return(FullMockAllocator(23)));
EXPECT_EQ(&x, Traits::allocate(mock, 7));
EXPECT_EQ(&y, Traits::allocate(mock, 13, static_cast<const void*>(&hint)));
EXPECT_EQ(1, trace);
Traits::construct(mock, &x, &trace);
EXPECT_EQ(1, trace);
Traits::destroy(mock, &x);
EXPECT_EQ(1, trace);
EXPECT_EQ(17u, Traits::max_size(mock));
EXPECT_EQ(0, mock.value);
EXPECT_EQ(23, Traits::select_on_container_copy_construction(mock).value);
}
TEST(AllocatorNoThrowTest, DefaultAllocator) {
#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW
EXPECT_TRUE(absl::default_allocator_is_nothrow::value);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment