Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make STL components not dependent on is_trivial(_v) #5202

Merged
merged 7 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions stl/inc/__msvc_string_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1344,9 +1344,10 @@ class basic_string_view { // wrapper for any kind of contiguous character buffer
"Bad char_traits for basic_string_view; N4950 [string.view.template.general]/1 "
"\"The program is ill-formed if traits::char_type is not the same type as charT.\"");

static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>,
"The character type of basic_string_view must be a non-array trivial standard-layout type. See N4950 "
"[strings.general]/1.");
static_assert(!is_array_v<_Elem> && is_trivially_copyable_v<_Elem> && is_trivially_default_constructible_v<_Elem>
&& is_standard_layout_v<_Elem>,
"The character type of basic_string_view must be a non-array trivially copyable standard-layout type T where "
"is_trivially_default_constructible_v<T> is true. See N5001 [strings.general]/1.");

using traits_type = _Traits;
using value_type = _Elem;
Expand Down
3 changes: 2 additions & 1 deletion stl/inc/execution
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,8 @@ struct _Parallel_choose_max_chunk {

template <class _Ty>
struct alignas(_Ty) alignas(size_t) alignas(_Atomic_counter_t) _Circular_buffer { // work stealing deque extent type
static_assert(is_trivial_v<_Ty>, "Work stealing deques work only with trivial operations");
static_assert(is_trivially_copyable_v<_Ty> && is_trivially_default_constructible_v<_Ty>,
"Work stealing deques work only with trivial operations");

size_t _Log_size;
_Atomic_counter_t _Ref_count;
Expand Down
11 changes: 2 additions & 9 deletions stl/inc/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -352,19 +352,12 @@ namespace filesystem {

inline constexpr _Is_slash_oper _Is_slash{};

template <class _Ty>
_NODISCARD _Ty _Unaligned_load(const void* _Ptr) { // load a _Ty from _Ptr
static_assert(is_trivial_v<_Ty>, "Unaligned loads require trivial types");
_Ty _Tmp;
_CSTD memcpy(&_Tmp, _Ptr, sizeof(_Tmp));
return _Tmp;
}

_NODISCARD inline bool _Is_drive_prefix(const wchar_t* const _First) {
// test if _First points to a prefix of the form X:
// pre: _First points to at least 2 wchar_t instances
// pre: Little endian
auto _Value = _Unaligned_load<unsigned int>(_First);
unsigned int _Value;
_CSTD memcpy(&_Value, _First, sizeof(_Value)); // load from possibly unaligned address
_Value &= 0xFFFF'FFDFu; // transform lowercase drive letters into uppercase ones
_Value -= (static_cast<unsigned int>(L':') << (sizeof(wchar_t) * CHAR_BIT)) | L'A';
return _Value < 26;
Expand Down
6 changes: 4 additions & 2 deletions stl/inc/memory
Original file line number Diff line number Diff line change
Expand Up @@ -2246,7 +2246,8 @@ struct _NODISCARD _Reverse_destroy_multidimensional_n_guard {

template <class _Ty, size_t _Size>
void _Uninitialized_copy_multidimensional(const _Ty (&_In)[_Size], _Ty (&_Out)[_Size]) {
if constexpr (is_trivial_v<_Ty>) {
using _Item = remove_all_extents_t<_Ty>;
if constexpr (conjunction_v<is_trivially_copy_constructible<_Item>, is_trivially_destructible<_Item>>) {
_STD _Copy_memmove_n(_In, _Size, _Out);
} else if constexpr (is_array_v<_Ty>) {
_Reverse_destroy_multidimensional_n_guard<_Ty> _Guard{_Out, 0};
Expand Down Expand Up @@ -2610,7 +2611,8 @@ struct _NODISCARD _Reverse_destroy_multidimensional_n_al_guard {
template <class _Ty, size_t _Size, class _Alloc>
void _Uninitialized_copy_multidimensional_al(const _Ty (&_In)[_Size], _Ty (&_Out)[_Size], _Alloc& _Al) {
using _Item = remove_all_extents_t<_Ty>;
if constexpr (conjunction_v<is_trivial<_Ty>, _Uses_default_construct<_Alloc, _Item*, const _Item&>>) {
if constexpr (conjunction_v<is_trivially_copy_constructible<_Item>, is_trivially_destructible<_Item>,
_Uses_default_construct<_Alloc, _Item*, const _Item&>>) {
_STD _Copy_memmove_n(_In, _Size, _Out);
} else if constexpr (is_array_v<_Ty>) {
_Reverse_destroy_multidimensional_n_al_guard<_Ty, _Alloc> _Guard{_Out, 0, _Al};
Expand Down
6 changes: 3 additions & 3 deletions stl/inc/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -2299,7 +2299,7 @@ _NODISCARD inline size_t _Fnv1a_append_bytes(size_t _Val, const unsigned char* c
template <class _Ty>
_NODISCARD size_t _Fnv1a_append_range(const size_t _Val, const _Ty* const _First,
const _Ty* const _Last) noexcept { // accumulate range [_First, _Last) into partial FNV-1a hash _Val
static_assert(is_trivial_v<_Ty>, "Only trivial types can be directly hashed.");
static_assert(is_trivially_copyable_v<_Ty>, "Only trivially copyable types can be directly hashed.");
const auto _Firstb = reinterpret_cast<const unsigned char*>(_First);
const auto _Lastb = reinterpret_cast<const unsigned char*>(_Last);
return _Fnv1a_append_bytes(_Val, _Firstb, static_cast<size_t>(_Lastb - _Firstb));
Expand All @@ -2308,7 +2308,7 @@ _NODISCARD size_t _Fnv1a_append_range(const size_t _Val, const _Ty* const _First
template <class _Kty>
_NODISCARD size_t _Fnv1a_append_value(
const size_t _Val, const _Kty& _Keyval) noexcept { // accumulate _Keyval into partial FNV-1a hash _Val
static_assert(is_trivial_v<_Kty>, "Only trivial types can be directly hashed.");
static_assert(is_trivially_copyable_v<_Kty>, "Only trivially copyable types can be directly hashed.");
return _Fnv1a_append_bytes(_Val, &reinterpret_cast<const unsigned char&>(_Keyval), sizeof(_Kty));
}

Expand All @@ -2320,7 +2320,7 @@ _NODISCARD size_t _Hash_representation(const _Kty& _Keyval) noexcept { // bitwis
template <class _Kty>
_NODISCARD size_t _Hash_array_representation(
const _Kty* const _First, const size_t _Count) noexcept { // bitwise hashes the representation of an array
static_assert(is_trivial_v<_Kty>, "Only trivial types can be directly hashed.");
static_assert(is_trivially_copyable_v<_Kty>, "Only trivially copyable types can be directly hashed.");
return _Fnv1a_append_bytes(
_FNV_offset_basis, reinterpret_cast<const unsigned char*>(_First), _Count * sizeof(_Kty));
}
Expand Down
12 changes: 7 additions & 5 deletions stl/inc/xstring
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,10 @@ private:
"N4950 [string.require]/3 requires that the supplied "
"char_traits character type match the string's character type.");

static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>,
"The character type of basic_string must be a non-array trivial standard-layout type. See N4950 "
"[strings.general]/1.");
static_assert(!is_array_v<_Elem> && is_trivially_copyable_v<_Elem> && is_trivially_default_constructible_v<_Elem>
&& is_standard_layout_v<_Elem>,
"The character type of basic_string must be a non-array trivially copyable standard-layout type T where "
"is_trivially_default_constructible_v<T> is true. See N5001 [strings.general]/1.");

public:
using traits_type = _Traits;
Expand Down Expand Up @@ -573,12 +574,13 @@ private:
// _String_val::_Bx::_Ptr (type is pointer)
// _String_val::_Mysize (type is size_type)
// _String_val::_Myres (type is size_type)
// N4950 [strings.general]/1 says _Elem must be trivial standard-layout, so memcpy is safe.
// N5001 [strings.general]/1 says _Elem must be trivially copyable standard-layout, so memcpy is safe.
// We need to ask if pointer is safe to memcpy.
// size_type must be an unsigned integral type so memcpy is safe.
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
// We also need to disable memcpy if the user has supplied _Traits, since
// they can observe traits::assign and similar.
static constexpr bool _Can_memcpy_val = _Is_specialization_v<_Traits, char_traits> && is_trivial_v<pointer>;
static constexpr bool _Can_memcpy_val =
_Is_specialization_v<_Traits, char_traits> && is_trivially_copyable_v<pointer>;
// This offset skips over the _Container_base members, if any
static constexpr size_t _Memcpy_val_offset = _Size_after_ebco_v<_Container_base>;
static constexpr size_t _Memcpy_val_size = sizeof(_Scary_val) - _Memcpy_val_offset;
Expand Down
7 changes: 4 additions & 3 deletions tests/std/tests/GH_000431_copy_move_family/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ struct TrivialStruct {
return i == right.i;
}
};
STATIC_ASSERT(is_trivial_v<TrivialStruct>);
STATIC_ASSERT(is_trivially_copyable_v<TrivialStruct>);
STATIC_ASSERT(is_trivially_default_constructible_v<TrivialStruct>);

struct TriviallyCopyableStruct {
int i;
Expand All @@ -49,7 +50,7 @@ struct TriviallyCopyableStruct {
}
};
STATIC_ASSERT(is_trivially_copyable_v<TriviallyCopyableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyCopyableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyCopyableStruct>);

struct TriviallyMovableStruct {
int i;
Expand All @@ -65,7 +66,7 @@ struct TriviallyMovableStruct {
}
};
STATIC_ASSERT(is_trivially_copyable_v<TriviallyMovableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyMovableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyMovableStruct>);

enum int_enum : int {};
enum char_enum : char {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,15 @@ void test_iter_cat_for_containers() {
struct TrivialStruct {
int i;
};
STATIC_ASSERT(is_trivial_v<TrivialStruct>);
STATIC_ASSERT(is_trivially_copyable_v<TrivialStruct>);
STATIC_ASSERT(is_trivially_default_constructible_v<TrivialStruct>);

struct TriviallyCopyableStruct {
int i;
TriviallyCopyableStruct();
};
STATIC_ASSERT(is_trivially_copyable_v<TriviallyCopyableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyCopyableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyCopyableStruct>);

struct TriviallyMovableStruct {
int i;
Expand All @@ -188,7 +189,7 @@ struct TriviallyMovableStruct {
TriviallyMovableStruct& operator=(TriviallyMovableStruct&&) = default;
};
STATIC_ASSERT(is_trivially_copyable_v<TriviallyMovableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyMovableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyMovableStruct>);

struct TriviallyConstructibleStruct {
int i;
Expand All @@ -204,7 +205,7 @@ STATIC_ASSERT(is_trivially_move_constructible_v<TriviallyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_copy_assignable_v<TriviallyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_move_assignable_v<TriviallyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyConstructibleStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyConstructibleStruct>);

struct TriviallyAssignableStruct {
int i;
Expand All @@ -220,7 +221,7 @@ STATIC_ASSERT(!is_trivially_move_constructible_v<TriviallyAssignableStruct>);
STATIC_ASSERT(is_trivially_copy_assignable_v<TriviallyAssignableStruct>);
STATIC_ASSERT(is_trivially_move_assignable_v<TriviallyAssignableStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyAssignableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyAssignableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyAssignableStruct>);

struct TriviallyCopyConstructibleStruct {
int i;
Expand All @@ -236,7 +237,7 @@ STATIC_ASSERT(!is_trivially_move_constructible_v<TriviallyCopyConstructibleStruc
STATIC_ASSERT(!is_trivially_copy_assignable_v<TriviallyCopyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_move_assignable_v<TriviallyCopyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyCopyConstructibleStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyCopyConstructibleStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyCopyConstructibleStruct>);

struct TriviallyCopyAssignableStruct {
int i;
Expand All @@ -253,7 +254,7 @@ STATIC_ASSERT(!is_trivially_move_constructible_v<TriviallyCopyAssignableStruct>)
STATIC_ASSERT(is_trivially_copy_assignable_v<TriviallyCopyAssignableStruct>);
STATIC_ASSERT(!is_trivially_move_assignable_v<TriviallyCopyAssignableStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyCopyAssignableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyCopyAssignableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyCopyAssignableStruct>);

struct TriviallyMoveConstructibleStruct {
int i;
Expand All @@ -269,7 +270,7 @@ STATIC_ASSERT(is_trivially_move_constructible_v<TriviallyMoveConstructibleStruct
STATIC_ASSERT(!is_trivially_copy_assignable_v<TriviallyMoveConstructibleStruct>);
STATIC_ASSERT(!is_trivially_move_assignable_v<TriviallyMoveConstructibleStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyMoveConstructibleStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyMoveConstructibleStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyMoveConstructibleStruct>);

struct TriviallyMoveAssignableStruct {
int i;
Expand All @@ -285,7 +286,7 @@ STATIC_ASSERT(!is_trivially_move_constructible_v<TriviallyMoveAssignableStruct>)
STATIC_ASSERT(!is_trivially_copy_assignable_v<TriviallyMoveAssignableStruct>);
STATIC_ASSERT(is_trivially_move_assignable_v<TriviallyMoveAssignableStruct>);
STATIC_ASSERT(!is_trivially_copyable_v<TriviallyMoveAssignableStruct>);
STATIC_ASSERT(!is_trivial_v<TriviallyMoveAssignableStruct>);
STATIC_ASSERT(!is_trivially_default_constructible_v<TriviallyMoveAssignableStruct>);

struct EmptyBase {};

Expand Down
5 changes: 3 additions & 2 deletions tests/std/tests/P0009R18_mdspan_layout_left/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ constexpr void check_members(const extents<IndexType, Extents...>& ext, index_se
using Ext = extents<IndexType, Extents...>;
using Mapping = layout_left::mapping<Ext>;

// layout_left meets the layout mapping policy requirements and is a trivial type
// layout_left meets the requirements of N5001 [mdspan.layout.policy.overview]/1
static_assert(check_layout_mapping_policy_requirements<layout_left, Ext>());
static_assert(is_trivial_v<layout_left>);
static_assert(is_trivially_copyable_v<layout_left>);
static_assert(is_trivially_default_constructible_v<layout_left>);

// layout_left::mapping<Ext> is a trivially copyable type that models regular for each Ext
static_assert(is_trivially_copyable_v<Mapping>);
Expand Down
5 changes: 3 additions & 2 deletions tests/std/tests/P0009R18_mdspan_layout_right/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ constexpr void check_members(const extents<IndexType, Extents...>& ext, index_se
using Ext = extents<IndexType, Extents...>;
using Mapping = layout_right::mapping<Ext>;

// layout_right meets the layout mapping policy requirements and is a trivial type
// layout_right meets the requirements of N5001 [mdspan.layout.policy.overview]/1
static_assert(check_layout_mapping_policy_requirements<layout_right, Ext>());
static_assert(is_trivial_v<layout_right>);
static_assert(is_trivially_copyable_v<layout_right>);
static_assert(is_trivially_default_constructible_v<layout_right>);

// layout_right::mapping<Ext> is a trivially copyable type that models regular for each Ext
static_assert(is_trivially_copyable_v<Mapping>);
Expand Down
5 changes: 3 additions & 2 deletions tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ constexpr void do_check_members(const extents<IndexType, Extents...>& ext,
using Strides = array<IndexType, sizeof...(Extents)>;
using Mapping = layout_stride::mapping<Ext>;

// layout_stride meets the layout mapping policy requirements and is a trivial type
// layout_stride meets the requirements of N5001 [mdspan.layout.policy.overview]/1
static_assert(check_layout_mapping_policy_requirements<layout_stride, Ext>());
static_assert(is_trivial_v<layout_stride>);
static_assert(is_trivially_copyable_v<layout_stride>);
static_assert(is_trivially_default_constructible_v<layout_stride>);

// layout_stride::mapping<Ext> is a trivially copyable type that models regular for each Ext
static_assert(is_trivially_copyable_v<Mapping>);
Expand Down
3 changes: 2 additions & 1 deletion tests/std/tests/P0009R18_mdspan_mdspan/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ struct TrivialAccessor {
};

static_assert(check_accessor_policy_requirements<TrivialAccessor<int>>());
static_assert(is_trivial_v<TrivialAccessor<int>>);
static_assert(is_trivially_copyable_v<TrivialAccessor<int>>);
static_assert(is_trivially_default_constructible_v<TrivialAccessor<int>>);

template <class Ext, class Layout, template <class> class AccessorTemplate>
constexpr void check_modeled_concepts_and_member_types() {
Expand Down
3 changes: 2 additions & 1 deletion tests/std/tests/P0323R12_expected/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ namespace test_unexpected {
namespace test_unexpect {
auto copy = unexpect;
static_assert(is_same_v<decltype(copy), unexpect_t>);
static_assert(is_trivial_v<unexpect_t>);
static_assert(is_trivially_copyable_v<unexpect_t>);
static_assert(is_trivially_default_constructible_v<unexpect_t>);
static_assert(is_empty_v<unexpect_t>);
} // namespace test_unexpect

Expand Down
2 changes: 1 addition & 1 deletion tests/std/tests/P0513R0_poisoning_the_hash/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ constexpr bool standard_hash_enabled() {
&& is_trivially_move_constructible_v<hash<T>> //
&& is_trivially_copy_assignable_v<hash<T>> //
&& is_trivially_move_assignable_v<hash<T>> //
&& is_trivial_v<hash<T>> // as a consequence of the above
&& is_trivially_copyable_v<hash<T>> //
&& is_same_v<typename hash<T>::argument_type, T> //
&& is_same_v<typename hash<T>::result_type, size_t> //
&& (noexcept(hash<T>{}(declval<const T&>())) == NoExcept) //
Expand Down
3 changes: 2 additions & 1 deletion tests/std/tests/P0896R4_P1614R2_comparisons/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ constexpr bool is_trivially_constexpr() {

// Not required, but likely portable nonetheless:
STATIC_ASSERT(std::is_empty_v<T>);
STATIC_ASSERT(std::is_trivial_v<T>);
STATIC_ASSERT(std::is_trivially_copyable_v<T>);
STATIC_ASSERT(std::is_trivially_default_constructible_v<T>);
STATIC_ASSERT(std::is_trivially_copy_constructible_v<T>);
STATIC_ASSERT(std::is_trivially_move_constructible_v<T>);
STATIC_ASSERT(std::is_trivially_copy_assignable_v<T>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,8 @@ namespace dangling_test {
namespace result_test {
using ranges::in_found_result, ranges::in_fun_result, ranges::in_in_result, ranges::in_out_result,
ranges::in_in_out_result, ranges::in_out_out_result, ranges::min_max_result;
using std::is_aggregate_v, std::is_convertible_v, std::is_trivial_v;
using std::is_aggregate_v, std::is_convertible_v, std::is_trivially_copyable_v,
std::is_trivially_default_constructible_v;

// Validate the result types are:
// * aggregates
Expand All @@ -559,14 +560,23 @@ namespace result_test {
static_assert(is_aggregate_v<in_out_out_result<int, int, int>>);
static_assert(is_aggregate_v<min_max_result<int>>);

// * trivial when parameter types are trivial
static_assert(is_trivial_v<in_found_result<int>>);
static_assert(is_trivial_v<in_fun_result<int, int>>);
static_assert(is_trivial_v<in_in_result<int, int>>);
static_assert(is_trivial_v<in_out_result<int, int>>);
static_assert(is_trivial_v<in_in_out_result<int, int, int>>);
static_assert(is_trivial_v<in_out_out_result<int, int, int>>);
static_assert(is_trivial_v<min_max_result<int>>);
// * trivially copyable when parameter types are trivially copyable
static_assert(is_trivially_copyable_v<in_found_result<int>>);
static_assert(is_trivially_copyable_v<in_fun_result<int, int>>);
static_assert(is_trivially_copyable_v<in_in_result<int, int>>);
static_assert(is_trivially_copyable_v<in_out_result<int, int>>);
static_assert(is_trivially_copyable_v<in_in_out_result<int, int, int>>);
static_assert(is_trivially_copyable_v<in_out_out_result<int, int, int>>);
static_assert(is_trivially_copyable_v<min_max_result<int>>);

// * trivially default constructible when parameter types are trivially default constructible
static_assert(is_trivially_default_constructible_v<in_found_result<int>>);
static_assert(is_trivially_default_constructible_v<in_fun_result<int, int>>);
static_assert(is_trivially_default_constructible_v<in_in_result<int, int>>);
static_assert(is_trivially_default_constructible_v<in_out_result<int, int>>);
static_assert(is_trivially_default_constructible_v<in_in_out_result<int, int, int>>);
static_assert(is_trivially_default_constructible_v<in_out_out_result<int, int, int>>);
static_assert(is_trivially_default_constructible_v<min_max_result<int>>);

// * usable with structured bindings
constexpr bool test_bindings_in_found_result() {
Expand Down
Loading