[libc++] Add the __is_replaceable type trait (#132408)

That type trait represents whether move-assigning an object is
equivalent to destroying it and then move-constructing a new one from
the same argument. This will be useful in a few places where we may want
to destroy + construct instead of doing an assignment, in particular
when implementing some container operations in terms of relocation.

This is effectively adding a library emulation of P2786R12's
is_replaceable trait, similarly to what we do for trivial relocation.
Eventually, we can replace this library emulation by the real
compiler-backed trait.

This is building towards #129328.
This commit is contained in:
Louis Dionne 2025-05-08 16:35:00 -04:00 committed by GitHub
parent c82e2f5c9e
commit 45d493b680
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 440 additions and 12 deletions

View File

@ -845,6 +845,7 @@ set(files
__type_traits/is_reference.h
__type_traits/is_reference_wrapper.h
__type_traits/is_referenceable.h
__type_traits/is_replaceable.h
__type_traits/is_same.h
__type_traits/is_scalar.h
__type_traits/is_signed.h

View File

@ -65,8 +65,10 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
friend _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT;
public:
// exception_ptr is basically a COW string.
// exception_ptr is basically a COW string so it is trivially relocatable.
// It is also replaceable because assignment has normal value semantics.
using __trivially_relocatable _LIBCPP_NODEBUG = exception_ptr;
using __replaceable _LIBCPP_NODEBUG = exception_ptr;
_LIBCPP_HIDE_FROM_ABI exception_ptr() _NOEXCEPT : __ptr_() {}
_LIBCPP_HIDE_FROM_ABI exception_ptr(nullptr_t) _NOEXCEPT : __ptr_() {}

View File

@ -30,6 +30,7 @@
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_replaceable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_constructible.h>
@ -471,6 +472,8 @@ public:
__conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value && __libcpp_is_trivially_relocatable<_Err>::value,
expected,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<_Tp> && __is_replaceable_v<_Err>, expected, void>;
template <class _Up>
using rebind = expected<_Up, error_type>;

View File

@ -56,8 +56,10 @@ _LIBCPP_HIDE_FROM_ABI const _Facet& use_facet(const locale&);
class _LIBCPP_EXPORTED_FROM_ABI locale {
public:
// locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor.
// locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor,
// so it is trivially relocatable. Like shared_ptr, it is also replaceable.
using __trivially_relocatable _LIBCPP_NODEBUG = locale;
using __replaceable _LIBCPP_NODEBUG = locale;
// types:
class _LIBCPP_EXPORTED_FROM_ABI facet;

View File

@ -316,8 +316,10 @@ public:
#endif
// A shared_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
// any bookkeeping, so it's always trivially relocatable.
// any bookkeeping, so it's always trivially relocatable. It is also replaceable because assignment just rebinds the
// shared_ptr to manage a different object.
using __trivially_relocatable _LIBCPP_NODEBUG = shared_ptr;
using __replaceable _LIBCPP_NODEBUG = shared_ptr;
private:
element_type* __ptr_;
@ -1211,8 +1213,9 @@ public:
#endif
// A weak_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
// any bookkeeping, so it's always trivially relocatable.
// any bookkeeping, so it's always trivially relocatable. It's also replaceable for the same reason.
using __trivially_relocatable _LIBCPP_NODEBUG = weak_ptr;
using __replaceable _LIBCPP_NODEBUG = weak_ptr;
private:
element_type* __ptr_;

View File

@ -39,6 +39,7 @@
#include <__type_traits/is_function.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_replaceable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_relocatable.h>
@ -144,6 +145,8 @@ public:
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
unique_ptr,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<pointer> && __is_replaceable_v<deleter_type>, unique_ptr, void>;
private:
_LIBCPP_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
@ -410,6 +413,8 @@ public:
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
unique_ptr,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<pointer> && __is_replaceable_v<deleter_type>, unique_ptr, void>;
private:
template <class _Up, class _OtherDeleter>

View File

@ -28,6 +28,7 @@
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_replaceable.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_destructible.h>
#include <__type_traits/is_trivially_relocatable.h>
@ -72,6 +73,10 @@ public:
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
__split_buffer,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<pointer> && __container_allocator_is_replaceable<__alloc_traits>::value,
__split_buffer,
void>;
pointer __first_;
pointer __begin_;

View File

@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H
#define _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H
#include <__config>
#include <__type_traits/enable_if.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
// A type is replaceable if, with `x` and `y` being different objects, `x = std::move(y)` is equivalent to:
//
// std::destroy_at(&x)
// std::construct_at(&x, std::move(y))
//
// This allows turning a move-assignment into a sequence of destroy + move-construct, which
// is often more efficient. This is especially relevant when the move-construct is in fact
// part of a trivial relocation from somewhere else, in which case there is a huge win.
//
// Note that this requires language support in order to be really effective, but we
// currently emulate the base template with something very conservative.
template <class _Tp, class = void>
struct __is_replaceable : is_trivially_copyable<_Tp> {};
template <class _Tp>
struct __is_replaceable<_Tp, __enable_if_t<is_same<_Tp, typename _Tp::__replaceable>::value> > : true_type {};
template <class _Tp>
inline const bool __is_replaceable_v = __is_replaceable<_Tp>::value;
// Determines whether an allocator member of a container is replaceable.
//
// First, we require the allocator type to be considered replaceable. If not, then something fishy might be
// happening. Assuming the allocator type is replaceable, we conclude replaceability of the allocator as a
// member of the container if the allocator always compares equal (in which case propagation doesn't matter),
// or if the allocator always propagates on assignment, which is required in order for move construction and
// assignment to be equivalent.
template <class _AllocatorTraits>
struct __container_allocator_is_replaceable
: integral_constant<bool,
__is_replaceable_v<typename _AllocatorTraits::allocator_type> &&
(_AllocatorTraits::is_always_equal::value ||
(_AllocatorTraits::propagate_on_container_move_assignment::value &&
_AllocatorTraits::propagate_on_container_copy_assignment::value))> {};
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H

View File

@ -32,6 +32,8 @@
#include <__type_traits/is_implicitly_default_constructible.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_replaceable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/nat.h>
@ -100,6 +102,7 @@ struct pair
__conditional_t<__libcpp_is_trivially_relocatable<_T1>::value && __libcpp_is_trivially_relocatable<_T2>::value,
pair,
void>;
using __replaceable _LIBCPP_NODEBUG = __conditional_t<__is_replaceable_v<_T1> && __is_replaceable_v<_T2>, pair, void>;
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;

View File

@ -55,6 +55,7 @@
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_replaceable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/type_identity.h>
@ -120,6 +121,10 @@ public:
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
vector,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<pointer> && __container_allocator_is_replaceable<__alloc_traits>::value,
vector,
void>;
static_assert(__check_valid_allocator<allocator_type>::value, "");
static_assert(is_same<typename allocator_type::value_type, value_type>::value,

View File

@ -134,6 +134,7 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
# include <__type_traits/is_const.h>
# include <__type_traits/is_constructible.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_swappable.h>
# include <__type_traits/is_trivially_relocatable.h>
@ -175,6 +176,7 @@ template <class _Tp, size_t _Size>
struct array {
using __trivially_relocatable _LIBCPP_NODEBUG =
__conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>;
using __replaceable _LIBCPP_NODEBUG = __conditional_t<__is_replaceable_v<_Tp>, array, void>;
// types:
using __self _LIBCPP_NODEBUG = array;

View File

@ -230,6 +230,7 @@ template <class T, class Allocator, class Predicate>
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_swappable.h>
# include <__type_traits/is_trivially_relocatable.h>
@ -530,6 +531,10 @@ public:
__libcpp_is_trivially_relocatable<__map>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
deque,
void>;
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<__map> && __container_allocator_is_replaceable<__alloc_traits>::value,
deque,
void>;
static_assert(is_nothrow_default_constructible<allocator_type>::value ==
is_nothrow_default_constructible<__pointer_allocator>::value,

View File

@ -272,6 +272,10 @@ module std_core [system] {
header "__type_traits/is_referenceable.h"
export std_core.type_traits.integral_constant
}
module is_replaceable {
header "__type_traits/is_replaceable.h"
export std_core.type_traits.integral_constant
}
module is_same {
header "__type_traits/is_same.h"
export std_core.type_traits.integral_constant

View File

@ -210,6 +210,7 @@ namespace std {
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_object.h>
# include <__type_traits/is_reference.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_scalar.h>
# include <__type_traits/is_swappable.h>
@ -590,6 +591,7 @@ public:
using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>;
private:
// Disable the reference extension using this static assert.

View File

@ -630,6 +630,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_standard_layout.h>
# include <__type_traits/is_trivially_constructible.h>
@ -755,6 +756,9 @@ public:
// external memory. In such cases, the destructor is responsible for unpoisoning
// the memory to avoid triggering false positives.
// Therefore it's crucial to ensure the destructor is called.
//
// However, it is replaceable since implementing move-assignment as a destroy + move-construct
// will maintain the right ASAN state.
using __trivially_relocatable = void;
# else
using __trivially_relocatable _LIBCPP_NODEBUG = __conditional_t<
@ -762,6 +766,10 @@ public:
basic_string,
void>;
# endif
using __replaceable _LIBCPP_NODEBUG =
__conditional_t<__is_replaceable_v<pointer> && __container_allocator_is_replaceable<__alloc_traits>::value,
basic_string,
void>;
# if _LIBCPP_HAS_ASAN && _LIBCPP_INSTRUMENTED_WITH_ASAN
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __asan_volatile_wrapper(pointer const& __ptr) const {

View File

@ -250,6 +250,7 @@ template <class... Types>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_reference.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_swappable.h>
# include <__type_traits/is_trivially_relocatable.h>
@ -462,8 +463,8 @@ template <class _Indx, class... _Tp>
struct __tuple_impl;
template <size_t... _Indx, class... _Tp>
struct _LIBCPP_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
: public __tuple_leaf<_Indx, _Tp>... {
struct _LIBCPP_DECLSPEC_EMPTY_BASES
__tuple_impl<__tuple_indices<_Indx...>, _Tp...> : public __tuple_leaf<_Indx, _Tp>... {
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_impl() noexcept(
__all<is_nothrow_default_constructible<_Tp>::value...>::value) {}
@ -555,6 +556,7 @@ class _LIBCPP_NO_SPECIALIZATIONS tuple {
public:
using __trivially_relocatable _LIBCPP_NODEBUG =
__conditional_t<_And<__libcpp_is_trivially_relocatable<_Tp>...>::value, tuple, void>;
using __replaceable _LIBCPP_NODEBUG = __conditional_t<_And<__is_replaceable<_Tp>...>::value, tuple, void>;
// [tuple.cnstr]

View File

@ -246,6 +246,7 @@ namespace std {
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_reference.h>
# include <__type_traits/is_replaceable.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_swappable.h>
# include <__type_traits/is_trivially_assignable.h>
@ -850,8 +851,7 @@ _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(
_Trait::_Available,
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) noexcept(
__all<is_nothrow_move_constructible_v<_Types>...>::value)
: __move_constructor(__valueless_t{}) {
__all<is_nothrow_move_constructible_v<_Types>...>::value) : __move_constructor(__valueless_t{}) {
this->__generic_construct(*this, std::move(__that));
} _LIBCPP_EAT_SEMICOLON);
@ -887,8 +887,9 @@ _LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_Trait::_Available,
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that)
: __copy_constructor(__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON);
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) : __copy_constructor(
__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON);
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_Trait::_Unavailable,
@ -1173,6 +1174,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES _LIBCPP_NO_SPECIALIZATIONS variant
public:
using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<_And<__libcpp_is_trivially_relocatable<_Types>...>::value, variant, void>;
using __replaceable _LIBCPP_NODEBUG = conditional_t<_And<__is_replaceable<_Types>...>::value, variant, void>;
template <bool _Dummy = true,
enable_if_t<__dependent_type<is_default_constructible<__first_type>, _Dummy>::value, int> = 0>
@ -1576,8 +1578,8 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) {
template <class... _Types>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto
swap(variant<_Types...>& __lhs,
variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) -> decltype(__lhs.swap(__rhs)) {
swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs)))
-> decltype(__lhs.swap(__rhs)) {
return __lhs.swap(__rhs);
}

View File

@ -0,0 +1,313 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
#include <__type_traits/is_replaceable.h>
#include <array>
#include <deque>
#include <exception>
#include <expected>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <type_traits>
#include <variant>
#include <vector>
#include "constexpr_char_traits.h"
#include "test_allocator.h"
#include "test_macros.h"
#ifndef TEST_HAS_NO_LOCALIZATION
# include <locale>
#endif
template <class T>
struct NonPropagatingStatefulMoveAssignAlloc : std::allocator<T> {
using propagate_on_container_move_assignment = std::false_type;
using is_always_equal = std::false_type;
template <class U>
struct rebind {
using other = NonPropagatingStatefulMoveAssignAlloc<U>;
};
};
template <class T>
struct NonPropagatingStatefulCopyAssignAlloc : std::allocator<T> {
using propagate_on_container_copy_assignment = std::false_type;
using is_always_equal = std::false_type;
template <class U>
struct rebind {
using other = NonPropagatingStatefulCopyAssignAlloc<U>;
};
};
template <class T>
struct NonPropagatingStatelessMoveAssignAlloc : std::allocator<T> {
using propagate_on_container_move_assignment = std::false_type;
using is_always_equal = std::true_type;
template <class U>
struct rebind {
using other = NonPropagatingStatelessMoveAssignAlloc<U>;
};
};
template <class T>
struct NonPropagatingStatelessCopyAssignAlloc : std::allocator<T> {
using propagate_on_container_copy_assignment = std::false_type;
using is_always_equal = std::true_type;
template <class U>
struct rebind {
using other = NonPropagatingStatelessCopyAssignAlloc<U>;
};
};
template <class T>
struct NonReplaceableStatelessAlloc : std::allocator<T> {
// Ensure that we don't consider an allocator that is a member of a container to be
// replaceable if it's not replaceable, even if it always compares equal and always propagates.
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_copy_assignment = std::true_type;
using is_always_equal = std::true_type;
NonReplaceableStatelessAlloc() = default;
NonReplaceableStatelessAlloc(NonReplaceableStatelessAlloc const&) {}
NonReplaceableStatelessAlloc(NonReplaceableStatelessAlloc&&) = default;
template <class U>
struct rebind {
using other = NonReplaceableStatelessAlloc<U>;
};
};
static_assert(!std::__is_replaceable<NonReplaceableStatelessAlloc<int> >::value, "");
static_assert(!std::__is_replaceable<test_allocator<char> >::value, ""); // we use that property below
struct Empty {};
static_assert(std::__is_replaceable<char>::value, "");
static_assert(std::__is_replaceable<int>::value, "");
static_assert(std::__is_replaceable<double>::value, "");
static_assert(std::__is_replaceable<Empty>::value, "");
struct TriviallyCopyable {
char c;
int i;
Empty s;
};
static_assert(std::__is_replaceable<TriviallyCopyable>::value, "");
struct NotTriviallyCopyable {
NotTriviallyCopyable(const NotTriviallyCopyable&);
~NotTriviallyCopyable();
};
static_assert(!std::__is_replaceable<NotTriviallyCopyable>::value, "");
struct MoveOnlyTriviallyCopyable {
MoveOnlyTriviallyCopyable(const MoveOnlyTriviallyCopyable&) = delete;
MoveOnlyTriviallyCopyable& operator=(const MoveOnlyTriviallyCopyable&) = delete;
MoveOnlyTriviallyCopyable(MoveOnlyTriviallyCopyable&&) = default;
MoveOnlyTriviallyCopyable& operator=(MoveOnlyTriviallyCopyable&&) = default;
};
static_assert(std::__is_replaceable<MoveOnlyTriviallyCopyable>::value, "");
struct CustomCopyAssignment {
CustomCopyAssignment(const CustomCopyAssignment&) = default;
CustomCopyAssignment(CustomCopyAssignment&&) = default;
CustomCopyAssignment& operator=(const CustomCopyAssignment&);
CustomCopyAssignment& operator=(CustomCopyAssignment&&) = default;
};
static_assert(!std::__is_replaceable<CustomCopyAssignment>::value, "");
struct CustomMoveAssignment {
CustomMoveAssignment(const CustomMoveAssignment&) = default;
CustomMoveAssignment(CustomMoveAssignment&&) = default;
CustomMoveAssignment& operator=(const CustomMoveAssignment&) = default;
CustomMoveAssignment& operator=(CustomMoveAssignment&&);
};
static_assert(!std::__is_replaceable<CustomMoveAssignment>::value, "");
// library-internal types
// ----------------------
// __split_buffer
static_assert(std::__is_replaceable<std::__split_buffer<int> >::value, "");
static_assert(std::__is_replaceable<std::__split_buffer<NotTriviallyCopyable> >::value, "");
static_assert(!std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value,
"");
static_assert(!std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value,
"");
static_assert(std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value,
"");
static_assert(std::__is_replaceable<std::__split_buffer<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value,
"");
// standard library types
// ----------------------
// array
static_assert(std::__is_replaceable<std::array<int, 0> >::value, "");
static_assert(std::__is_replaceable<std::array<NotTriviallyCopyable, 0> >::value, "");
static_assert(std::__is_replaceable<std::array<std::unique_ptr<int>, 0> >::value, "");
static_assert(std::__is_replaceable<std::array<int, 1> >::value, "");
static_assert(!std::__is_replaceable<std::array<NotTriviallyCopyable, 1> >::value, "");
static_assert(std::__is_replaceable<std::array<std::unique_ptr<int>, 1> >::value, "");
// basic_string
struct MyChar {
char c;
};
template <class T>
struct NotReplaceableCharTraits : constexpr_char_traits<T> {
NotReplaceableCharTraits(const NotReplaceableCharTraits&);
NotReplaceableCharTraits& operator=(const NotReplaceableCharTraits&);
~NotReplaceableCharTraits();
};
static_assert(std::__is_replaceable<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value,
"");
static_assert(
std::__is_replaceable<std::basic_string<char, NotReplaceableCharTraits<char>, std::allocator<char> > >::value, "");
static_assert(
std::__is_replaceable<std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value,
"");
static_assert(!std::__is_replaceable<std::basic_string<char, std::char_traits<char>, test_allocator<char> > >::value,
"");
static_assert(!std::__is_replaceable<
std::basic_string<char, std::char_traits<char>, NonReplaceableStatelessAlloc<char> > >::value,
"");
static_assert(std::__is_replaceable<
std::basic_string<MyChar, NotReplaceableCharTraits<MyChar>, std::allocator<MyChar> > >::value,
"");
static_assert(
!std::__is_replaceable<
std::basic_string<char, std::char_traits<char>, NonPropagatingStatefulCopyAssignAlloc<char> > >::value,
"");
static_assert(
!std::__is_replaceable<
std::basic_string<char, std::char_traits<char>, NonPropagatingStatefulMoveAssignAlloc<char> > >::value,
"");
static_assert(
std::__is_replaceable<
std::basic_string<char, std::char_traits<char>, NonPropagatingStatelessCopyAssignAlloc<char> > >::value,
"");
static_assert(
std::__is_replaceable<
std::basic_string<char, std::char_traits<char>, NonPropagatingStatelessMoveAssignAlloc<char> > >::value,
"");
// deque
static_assert(std::__is_replaceable<std::deque<int> >::value, "");
static_assert(std::__is_replaceable<std::deque<NotTriviallyCopyable> >::value, "");
static_assert(!std::__is_replaceable<std::deque<int, test_allocator<int> > >::value, "");
static_assert(!std::__is_replaceable<std::deque<int, NonReplaceableStatelessAlloc<int> > >::value, "");
static_assert(!std::__is_replaceable<std::deque<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value, "");
static_assert(!std::__is_replaceable<std::deque<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value, "");
static_assert(std::__is_replaceable<std::deque<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value, "");
static_assert(std::__is_replaceable<std::deque<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value, "");
// exception_ptr
#ifndef _LIBCPP_ABI_MICROSOFT
static_assert(std::__is_replaceable<std::exception_ptr>::value, "");
#endif
// expected
#if TEST_STD_VER >= 23
static_assert(std::__is_replaceable<std::expected<int, int> >::value);
static_assert(!std::__is_replaceable<std::expected<CustomCopyAssignment, int>>::value);
static_assert(!std::__is_replaceable<std::expected<int, CustomCopyAssignment>>::value);
static_assert(!std::__is_replaceable<std::expected<CustomCopyAssignment, CustomCopyAssignment>>::value);
#endif
// locale
#ifndef TEST_HAS_NO_LOCALIZATION
static_assert(std::__is_replaceable<std::locale>::value, "");
#endif
// optional
#if TEST_STD_VER >= 17
static_assert(std::__is_replaceable<std::optional<int>>::value, "");
static_assert(!std::__is_replaceable<std::optional<CustomCopyAssignment>>::value, "");
#endif
// pair
static_assert(std::__is_replaceable<std::pair<int, int> >::value, "");
static_assert(!std::__is_replaceable<std::pair<CustomCopyAssignment, int> >::value, "");
static_assert(!std::__is_replaceable<std::pair<int, CustomCopyAssignment> >::value, "");
static_assert(!std::__is_replaceable<std::pair<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
// shared_ptr
static_assert(std::__is_replaceable<std::shared_ptr<int> >::value, "");
// tuple
#if TEST_STD_VER >= 11
static_assert(std::__is_replaceable<std::tuple<> >::value, "");
static_assert(std::__is_replaceable<std::tuple<int> >::value, "");
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment> >::value, "");
static_assert(std::__is_replaceable<std::tuple<int, int> >::value, "");
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment, int> >::value, "");
static_assert(!std::__is_replaceable<std::tuple<int, CustomCopyAssignment> >::value, "");
static_assert(!std::__is_replaceable<std::tuple<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
#endif // TEST_STD_VER >= 11
// unique_ptr
struct NonReplaceableDeleter {
NonReplaceableDeleter(const NonReplaceableDeleter&);
NonReplaceableDeleter& operator=(const NonReplaceableDeleter&);
~NonReplaceableDeleter();
template <class T>
void operator()(T*);
};
struct NonReplaceablePointer {
struct pointer {
pointer(const pointer&);
pointer& operator=(const pointer&);
~pointer();
};
template <class T>
void operator()(T*);
};
static_assert(std::__is_replaceable<std::unique_ptr<int> >::value, "");
static_assert(std::__is_replaceable<std::unique_ptr<CustomCopyAssignment> >::value, "");
static_assert(std::__is_replaceable<std::unique_ptr<int[]> >::value, "");
static_assert(!std::__is_replaceable<std::unique_ptr<int, NonReplaceableDeleter> >::value, "");
static_assert(!std::__is_replaceable<std::unique_ptr<int[], NonReplaceableDeleter> >::value, "");
static_assert(!std::__is_replaceable<std::unique_ptr<int, NonReplaceablePointer> >::value, "");
static_assert(!std::__is_replaceable<std::unique_ptr<int[], NonReplaceablePointer> >::value, "");
// variant
#if TEST_STD_VER >= 17
static_assert(std::__is_replaceable<std::variant<int> >::value, "");
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment> >::value, "");
static_assert(std::__is_replaceable<std::variant<int, int> >::value, "");
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment, int> >::value, "");
static_assert(!std::__is_replaceable<std::variant<int, CustomCopyAssignment> >::value, "");
static_assert(!std::__is_replaceable<std::variant<CustomCopyAssignment, CustomCopyAssignment> >::value, "");
#endif // TEST_STD_VER >= 17
// vector
static_assert(std::__is_replaceable<std::vector<int> >::value, "");
static_assert(std::__is_replaceable<std::vector<CustomCopyAssignment> >::value, "");
static_assert(!std::__is_replaceable<std::vector<int, test_allocator<int> > >::value, "");
static_assert(!std::__is_replaceable<std::vector<int, NonReplaceableStatelessAlloc<int> > >::value, "");
static_assert(!std::__is_replaceable<std::vector<int, NonPropagatingStatefulCopyAssignAlloc<int> > >::value, "");
static_assert(!std::__is_replaceable<std::vector<int, NonPropagatingStatefulMoveAssignAlloc<int> > >::value, "");
static_assert(std::__is_replaceable<std::vector<int, NonPropagatingStatelessCopyAssignAlloc<int> > >::value, "");
static_assert(std::__is_replaceable<std::vector<int, NonPropagatingStatelessMoveAssignAlloc<int> > >::value, "");
// weak_ptr
static_assert(std::__is_replaceable<std::weak_ptr<CustomCopyAssignment> >::value, "");
// TODO: Mark all the replaceable STL types as such