Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c4a3ccf
[libc++] Implement ranges::iota and ranges::out_value_result
jamesETsmith Sep 28, 2023
1b808ec
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Sep 28, 2023
574c94c
[libc++] Implement ranges::iota: update module and docs info
jamesETsmith Sep 29, 2023
e4cfe00
[libc++] Implement ranges::iota: fixing formatting issues for out_val…
jamesETsmith Sep 29, 2023
7cdfea2
[libc++] Implement ranges::iota: Cleaning up tests for ranges::iota a…
jamesETsmith Oct 7, 2023
a1d015c
[libc++] Implement ranges::iota: Cleaning up tests for ranges::iota a…
jamesETsmith Oct 7, 2023
ab4c67d
[libc++] Implement ranges::iota: Using clang-format-17 to make CI che…
jamesETsmith Oct 7, 2023
c44d7d6
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Oct 7, 2023
a455f42
[libc++] Implement ranges::iota: Adding helper function to implementa…
jamesETsmith Oct 25, 2023
08e3c77
[libc++] Implement ranges::iota: Adding tests to ranges_robust_agains…
jamesETsmith Oct 26, 2023
e658ec2
[libc++] Implement ranges::iota: Merging with main and handling confl…
jamesETsmith Oct 26, 2023
67fcac1
[libc++] Implement ranges::iota: Fixing formatting problems
jamesETsmith Oct 26, 2023
b132784
[libc++] Implement ranges::iota: Addressing comments about out_value_…
jamesETsmith Oct 26, 2023
88e271a
[libc++] Implement ranges::iota: Updating Proxy<T> in test_iterators.…
jamesETsmith Oct 27, 2023
ff5bf79
[libc++] Implement ranges::iota: Fixing some of the buildkite errors,…
jamesETsmith Oct 28, 2023
b8f07b8
[libc++] Implement ranges::iota: Explicitly adding #include for range…
jamesETsmith Oct 30, 2023
a4d34fd
[libc++] Implement ranges::iota: Explicitly adding #include for range…
jamesETsmith Oct 30, 2023
334088f
:Merge branch 'main' of https://github.com/llvm/llvm-project into ran…
jamesETsmith Oct 30, 2023
8153244
[libc++] Implement ranges::iota: Adding #ifdef _LIBCPP_STD_VER >= 23 …
jamesETsmith Oct 31, 2023
a872e39
[libc++] Implement ranges::iota: Missing a few more #if TEST_STD_VER …
jamesETsmith Nov 4, 2023
0f538aa
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Nov 4, 2023
b84859b
[libc++] Implement ranges::iota: Removing several of the ranges_robus…
jamesETsmith Nov 5, 2023
3bd4c6d
[libc++] Implement ranges::iota: Updating with upstream main and reso…
jamesETsmith Nov 15, 2023
6be79e2
[libc++] Implement ranges::iota: Updating with upstream and fixing co…
jamesETsmith Dec 13, 2023
7cde8ed
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Dec 16, 2023
074d685
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Dec 18, 2023
72125ea
[libc++] Implement ranges::iota: Updating with upstream and adding st…
jamesETsmith Dec 18, 2023
ce909e8
Merge branch 'main' of https://github.com/llvm/llvm-project into rang…
jamesETsmith Dec 19, 2023
028ce92
[libc++] Implement ranges::iota: Fixing formatting problems with test…
jamesETsmith Dec 19, 2023
b3f260e
[libc++] Implement ranges::iota: Improving ranges::iota tests and doc…
jamesETsmith Jan 5, 2024
be7faa6
[libc++] Implement ranges::iota: Removing unnecessary namespaces in r…
jamesETsmith Jan 6, 2024
20b848b
[libc++] Implement ranges::iota: Refactoring some of Proxy's member f…
jamesETsmith Jan 15, 2024
2803493
[libc++] Implement ranges::iota: Undoing some problematic additions t…
jamesETsmith Jan 15, 2024
5f0389c
[libc++] Implement ranges::iota: Fixing review comments on ranges.iot…
jamesETsmith Apr 28, 2024
79d6cc5
[libc++] Implementing ranges::iota: Adding user-defined type tests an…
jamesETsmith Apr 28, 2024
07b8183
[libc++] Implementing ranges::iota: Reverting to original strategy fo…
jamesETsmith Apr 28, 2024
3d5ad73
[libc++] Implementing ranges::iota: Updating branch with llvm-project…
jamesETsmith Apr 30, 2024
fd81400
[libc++] Implementing ranges::iota: Needed to include __undef_macros …
jamesETsmith May 3, 2024
3285f4e
[libc++] Implementing ranges::iota: Fixing autogenerated version.vers…
jamesETsmith May 3, 2024
cea6379
Merge branch 'main' of github.com:llvm/llvm-project into ranges_iota
jamesETsmith May 7, 2024
3f0670f
[libc++] Implementing ranges::iota: Simplifying some tests, adding st…
jamesETsmith May 22, 2024
a7e77b1
[libc++] Implementing ranges::iota: Updating with upstream main and r…
jamesETsmith Jul 22, 2024
d50b7a1
[libc++] Implementing ranges::iota: Accidentally manually updated the…
jamesETsmith Jul 24, 2024
b4008ad
[libc++] Implementing ranges::iota: Cleanup and addressing comments f…
jamesETsmith Jul 26, 2024
240d4f0
[libc++] Implementing ranges::iota: Updating with upstream main
jamesETsmith Mar 27, 2025
8f5ffc3
[libc++] Implementing ranges::iota: Including out_value_result got st…
jamesETsmith Mar 27, 2025
b99c8d8
[libc++] Implementing ranges::iota: Needed to add __iterator/concepts…
jamesETsmith Apr 1, 2025
dc29b80
[libc++] implementing ranges::iota: Fixing the Cxx23Paper.csv doc for…
jamesETsmith Apr 2, 2025
e325de4
[libc++] implementing ranges::iota: Switching the wording in Cxx23Pap…
jamesETsmith Apr 2, 2025
913a8d8
[libc++] implementing ranges::iota: the header for out_value_result.h…
jamesETsmith Apr 3, 2025
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
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk_by`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_iota`` *unimplemented*
``__cpp_lib_ranges_iota`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_join_with`` *unimplemented*
--------------------------------------------------- -----------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|"
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress|","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ set(files
__algorithm/next_permutation.h
__algorithm/none_of.h
__algorithm/nth_element.h
__algorithm/out_value_result.h
__algorithm/partial_sort.h
__algorithm/partial_sort_copy.h
__algorithm/partition.h
Expand Down Expand Up @@ -561,6 +562,7 @@ set(files
__numeric/partial_sum.h
__numeric/pstl_reduce.h
__numeric/pstl_transform_reduce.h
__numeric/ranges_iota.h
__numeric/reduce.h
__numeric/transform_exclusive_scan.h
__numeric/transform_inclusive_scan.h
Expand Down
56 changes: 56 additions & 0 deletions libcxx/include/__algorithm/out_value_result.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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___ALGORITHM_OUT_VALUE_RESULT_H
#define _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H

#include <__concepts/convertible_to.h>
#include <__config>
#include <__utility/move.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 23

namespace ranges {

template <class _OutIter1, class _ValType1>
struct out_value_result {
_LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out;
_LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value;

template <class _OutIter2, class _ValType2>
requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
constexpr operator out_value_result<_OutIter2, _ValType2>() const& {
return {out, value};
}

template <class _OutIter2, class _ValType2>
requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
constexpr operator out_value_result<_OutIter2, _ValType2>() && {
return {std::move(out), std::move(value)};
}
};

} // namespace ranges

#endif // _LIBCPP_STD_VER >= 23

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
54 changes: 54 additions & 0 deletions libcxx/include/__numeric/ranges_iota.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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___NUMERIC_RANGES_IOTA_H
#define _LIBCPP___NUMERIC_RANGES_IOTA_H

#include <__algorithm/out_value_result.h>
#include <__config>
#include <__ranges/concepts.h>
#include <__utility/as_const.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 23
namespace ranges {
template <typename _Out, typename _Tp>
using iota_result = ranges::out_value_result<_Out, _Tp>;

struct __iota_fn {
template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
requires indirectly_writable<_Out, const _Tp&>
constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) {

same below

Copy link
Author

@jamesETsmith jamesETsmith Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philnik777, I'm happy to make these changes, but could you explain why static is more appropriate than const here?

As an alternative, after I've added the helper function, I could make that static and leave the public functions as const like it says in the original paper. I stumbled on this strategy (it's used by ranges::for_each and ranges::generate at least) while looking at how other libcxx helper functions were structured.

EDIT: I realized it would probably be easier to just implement this second strategy (in a455f42) and see what you think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static operator() has been introduced in C++23, so we can't (currently) use it for C++20 algorithms. Otherwise we would also use it there. The simple reason to use it is that we don't care about the object and the static version has slightly better code gen (https://godbolt.org/z/8E1f77q6f).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, I wasn't aware that static operator() was introduced in C++23. That clears things up, thanks for the example.

while (__first != __last) {
*__first = std::as_const(__value);
++__first;
++__value;
}
return {std::move(__first), std::move(__value)};
}

template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range>
constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const {
return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably run into problems with this when updating the robust_against_* tests. You should move the algorithm itself into a helper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this would cause problems?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I've been slow on this. @philnik777 do you have any suggestions/comments about this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have "robust" tests to make sure we're not copying or moving comparators and projections, but I don't think we do that for "values" as is the case here. Whether it's an omission or not is a separate question, but as it stands, I think the original form of this code (forwarding directly from the ranges overload to the iterator overload) should work -- can you please check that, @jamesETsmith?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I had rechecked that the original implementation had caused problems with the robust_against_* tests, but after checking again today, it doesn't seem to cause any problems.

As a result, I've reverted to the original strategy at @cjdb and @var-const's suggestion.

}
};

inline constexpr __iota_fn iota{};
} // namespace ranges

#endif // _LIBCPP_STD_VER >= 23

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H
4 changes: 4 additions & 0 deletions libcxx/include/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ namespace ranges {
template <class I>
struct in_found_result; // since C++20

template <class O, class T>
struct out_value_result; // since C++23

template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less> // since C++20
constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {});
Expand Down Expand Up @@ -1817,6 +1820,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/next_permutation.h>
#include <__algorithm/none_of.h>
#include <__algorithm/nth_element.h>
#include <__algorithm/out_value_result.h>
#include <__algorithm/partial_sort.h>
#include <__algorithm/partial_sort_copy.h>
#include <__algorithm/partition.h>
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,7 @@ module std_private_numeric_pstl_transform_reduce [system] {
export *
}
module std_private_numeric_reduce [system] { header "__numeric/reduce.h" }
module std_private_numeric_ranges_iota [system] { header "__numeric/ranges_iota.h" }
module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
module std_private_numeric_transform_reduce [system] { header "__numeric/transform_reduce.h" }
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/numeric
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ template<class T>
#include <__numeric/partial_sum.h>
#include <__numeric/pstl_reduce.h>
#include <__numeric/pstl_transform_reduce.h>
#include <__numeric/ranges_iota.h>
#include <__numeric/reduce.h>
#include <__numeric/transform_exclusive_scan.h>
#include <__numeric/transform_inclusive_scan.h>
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_ranges_as_rvalue 202207L
// # define __cpp_lib_ranges_chunk 202202L
# define __cpp_lib_ranges_chunk_by 202202L
// # define __cpp_lib_ranges_iota 202202L
# define __cpp_lib_ranges_iota 202202L
// # define __cpp_lib_ranges_join_with 202202L
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
Expand Down
2 changes: 1 addition & 1 deletion libcxx/modules/std/algorithm.inc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export namespace std {
using std::ranges::in_out_result;
// using std::ranges::in_value_result;
using std::ranges::min_max_result;
// using std::ranges::out_value_result;
using std::ranges::out_value_result;
} // namespace ranges

// [alg.nonmodifying], non-modifying sequence operations
Expand Down
8 changes: 6 additions & 2 deletions libcxx/modules/std/numeric.inc
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ export namespace std {
using std::iota;

namespace ranges {
// using std::ranges::iota_result;
// using std::ranges::iota;

#if _LIBCPP_STD_VER >= 23
using std::ranges::iota;
using std::ranges::iota_result;
#endif // _LIBCPP_STD_VER >= 23

} // namespace ranges

// [numeric.ops.gcd], greatest common divisor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//===----------------------------------------------------------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've started working on this in https://reviews.llvm.org/D121436 (and didn't really work on it because nobody knew what it was for). You should probably make sure that any comments there are addressed here and add any additional tests from there.

//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, C++20

// template <class O, class T>
// struct out_value_result;

#include <algorithm>
#include <cassert>
#include <type_traits>

#include "MoveOnly.h"

//
// Helper structs
//

// only explicit construction
struct IterTypeExplicit {
explicit IterTypeExplicit(int*);
};

// implicit construction
struct IterTypeImplicit {
IterTypeImplicit(int*);
};

struct IterTypeImplicitRef {
IterTypeImplicitRef(int&);
};

struct NotConvertible {};

template <class T>
struct ConvertibleFrom {
constexpr ConvertibleFrom(T c) : content{c} {}
T content;
};

//
constexpr void test_constraints() {
// requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
static_assert(
std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>);

// test failure when implicit conversion isn't allowed
static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>,
std::ranges::out_value_result<int*, int>>);

// test success when implicit conversion is allowed, checking combinations of value, reference, and const
static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
std::ranges::out_value_result<int*, int>>);
static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
std::ranges::out_value_result<int*, int> const>);
static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
std::ranges::out_value_result<int*, int>&>);
static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
std::ranges::out_value_result<int*, int> const&>);

static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>,
std::ranges::out_value_result<int, int>&>);

// has to be convertible via const&
static_assert(
std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
static_assert(
std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
static_assert(
std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&&,
std::ranges::out_value_result<long, long>>);

// should be move constructible
static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);

// conversions should not work if there is no conversion
static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>,
std::ranges::out_value_result<int, NotConvertible>>);
static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
std::ranges::out_value_result<NotConvertible, int>>);
}

// Test results
constexpr bool test() {
{
std::ranges::out_value_result<double, int> res{10, 1};
assert(res.out == 10);
assert(res.value == 1);
std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res;
assert(res2.out.content == 10);
assert(res2.value.content == 1);
}
{
std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10};
assert(res.out.get() == 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: consider passing the value explicitly (honestly, I think MoveOnly having a default non-zero value is a bit confusing).

assert(res.value == 10);
auto res2 = std::move(res);
assert(res.out.get() == 0);
assert(res.value == 10);
assert(res2.out.get() == 1);
assert(res2.value == 10);
}
{
auto [out, val] = std::ranges::out_value_result<int, int>{1, 2};
assert(out == 1);
assert(val == 2);
}

return true;
}

int main(int, char**) {
test_constraints();
test();
static_assert(test());
return 0;
}
Loading