From 6b9405f7fa84946901192e326100b581fdabc7fa Mon Sep 17 00:00:00 2001 From: Yong Gyu Lee Date: Sat, 24 Aug 2024 02:35:45 +0900 Subject: [PATCH] Fix n refactor (#40) * Fix forward_like * Update override_cvref.h * Fix typo * Fix typo :( * Update ref_view.h * Fix as_const_view::as_const_view() * Fix drop_view handling repeat_view * Fix drop_view::drop_view * Fix enumerate_view::enumerate_view * Fix filter_view:: filter_view * Fix iota_view::end() for unreachble_sentinel * Fix join_with_view::join_with_view * Fix views::repeat using remove_cvref_t instead of decay_t * Fix missing constraints on repeat_view::repeat_view(const W&) * Fix missing constraints for single_view * Optimize views::take handling repeat_view * Fix views::take_while using remove_cvref_t instead of decay_t * Update zip_transform.h * Rollback to good-old Dummy-void constraints * Update range_adaptor.h * Hide drop_while_view::cached_begin * Fix lazy_split_view * Update split_view.h --- .../__concepts/derived_from_single_crtp.h | 2 +- include/preview/__functional/bind_partial.h | 2 +- include/preview/__ranges/range_adaptor.h | 14 +- include/preview/__ranges/ref_view.h | 16 +- .../preview/__ranges/views/as_const_view.h | 2 +- include/preview/__ranges/views/drop.h | 159 +++++++--------- include/preview/__ranges/views/drop_view.h | 2 +- .../preview/__ranges/views/drop_while_view.h | 7 +- .../preview/__ranges/views/enumerate_view.h | 2 +- include/preview/__ranges/views/filter_view.h | 13 +- include/preview/__ranges/views/iota_view.h | 27 +-- .../preview/__ranges/views/join_with_view.h | 2 +- .../preview/__ranges/views/lazy_split_view.h | 173 ++++++++++++------ include/preview/__ranges/views/repeat.h | 19 +- include/preview/__ranges/views/repeat_view.h | 4 + include/preview/__ranges/views/single.h | 7 +- include/preview/__ranges/views/split_view.h | 16 +- include/preview/__ranges/views/take.h | 114 ++++++------ include/preview/__ranges/views/take_view.h | 118 ++++++------ include/preview/__ranges/views/take_while.h | 14 +- .../preview/__ranges/views/transform_view.h | 21 ++- .../preview/__ranges/views/zip_transform.h | 30 +-- .../preview/__type_traits/override_cvref.h | 11 +- include/preview/__utility/forward_like.h | 11 +- test/ranges_views.cc | 8 + test/utility.cc | 101 +++++++++- 26 files changed, 542 insertions(+), 353 deletions(-) diff --git a/include/preview/__concepts/derived_from_single_crtp.h b/include/preview/__concepts/derived_from_single_crtp.h index dc29bcc7..4705d589 100644 --- a/include/preview/__concepts/derived_from_single_crtp.h +++ b/include/preview/__concepts/derived_from_single_crtp.h @@ -30,7 +30,7 @@ template< bool = is_referencable::value /* true */ > struct derived_from_single_crtp_impl -#if defined(_MSC_VER) && _MSC_VER < 1930 // Ambigious casting is allowed until Visutal Studio 2022 +#if defined(_MSC_VER) && _MSC_VER < 1930 // Ambiguous casting is allowed until Visual Studio 2022 : has_typename_preview_derived {}; #else : is_invocable_r< diff --git a/include/preview/__functional/bind_partial.h b/include/preview/__functional/bind_partial.h index 625ff83d..d00bc387 100644 --- a/include/preview/__functional/bind_partial.h +++ b/include/preview/__functional/bind_partial.h @@ -41,7 +41,7 @@ class bind_partial { std::is_constructible... >::value, int> = 0> constexpr explicit bind_partial(F&& f, Args&&... args) - : pair_(std::piecewise_construct, std::forward_as_tuple(std::forward(f)), std::forward_as_tuple(std::forward(args))...) {} + : pair_(std::piecewise_construct, std::forward_as_tuple(std::forward(f)), std::forward_as_tuple(std::forward(args)...)) {} template::value, int> = 0> constexpr decltype(auto) operator()(CallArgs&&... call_args) & diff --git a/include/preview/__ranges/range_adaptor.h b/include/preview/__ranges/range_adaptor.h index 2d4ef7bc..e0379fe6 100644 --- a/include/preview/__ranges/range_adaptor.h +++ b/include/preview/__ranges/range_adaptor.h @@ -34,28 +34,28 @@ class range_adaptor : public range_adaptor_closure::value, int> = 0> constexpr decltype(auto) operator()(R&& r) & { - return call(*this, std::forward(r), std::index_sequence_for{}); + return call(std::forward(r), args_, std::index_sequence_for{}); } template::value, int> = 0> constexpr decltype(auto) operator()(R&& r) const & { - return call(*this, std::forward(r), std::index_sequence_for{}); + return call(std::forward(r), args_, std::index_sequence_for{}); } template::value, int> = 0> constexpr decltype(auto) operator()(R&& r) && { - return call(std::move(*this), std::forward(r), std::index_sequence_for{}); + return call(std::forward(r), std::move(args_), std::index_sequence_for{}); } template::value, int> = 0> constexpr decltype(auto) operator()(R&& r) const && { - return call(std::move(*this), std::forward(r), std::index_sequence_for{}); + return call(std::forward(r), std::move(args_), std::index_sequence_for{}); } private: - template - static constexpr decltype(auto) call(This&& thiz, R&& r, std::index_sequence) { - return Niebloid{}(std::forward(r), std::get(std::forward(thiz).args_)...); + template + static constexpr decltype(auto) call(R&& r, ArgTuple&& tuple, std::index_sequence) { + return Niebloid{}(std::forward(r), std::get(std::forward(tuple))...); } std::tuple args_; diff --git a/include/preview/__ranges/ref_view.h b/include/preview/__ranges/ref_view.h index a433bb18..545c617b 100644 --- a/include/preview/__ranges/ref_view.h +++ b/include/preview/__ranges/ref_view.h @@ -26,16 +26,30 @@ namespace preview { namespace ranges { +namespace detail { +namespace ref_view_fn { + +template static void fun(R&); +template static void fun(R&&) = delete; + +template +struct fallback_to_lref : std::false_type {}; +template +struct fallback_to_lref(std::declval()))>> : std::true_type {}; + +} // namespace ref_view_fn +} // namespace detail template class ref_view : public view_interface> { public: + static_assert(range::value, "Constraints not satisfied"); static_assert(std::is_object::value, "Constraints not satisfied"); template, convertible_to, - std::is_lvalue_reference + detail::ref_view_fn::fallback_to_lref >::value, int> = 0> PREVIEW_CONSTEXPR_AFTER_CXX17 ref_view(T&& t) noexcept : r_(preview::addressof(static_cast(std::forward(t)))) {} diff --git a/include/preview/__ranges/views/as_const_view.h b/include/preview/__ranges/views/as_const_view.h index 25a8ae4a..d7acc350 100644 --- a/include/preview/__ranges/views/as_const_view.h +++ b/include/preview/__ranges/views/as_const_view.h @@ -70,7 +70,7 @@ class as_const_view : public view_interface> { } private: - V base_; + V base_{}; }; #if __cplusplus >= 201703L diff --git a/include/preview/__ranges/views/drop.h b/include/preview/__ranges/views/drop.h index 5f71a2a8..d601fc76 100644 --- a/include/preview/__ranges/views/drop.h +++ b/include/preview/__ranges/views/drop.h @@ -14,6 +14,9 @@ #if PREVIEW_CXX_VERSION >= 17 #include #endif +#if PREVIEW_CXX_VERSION >= 20 +#include +#endif #include "preview/__concepts/different_from.h" #include "preview/__ranges/range.h" @@ -31,6 +34,7 @@ #include "preview/__type_traits/detail/return_category.h" #include "preview/__type_traits/is_specialization.h" #include "preview/__type_traits/remove_cvref.h" +#include "preview/__utility/forward_like.h" #include "preview/__utility/to_unsigned_like.h" namespace preview { @@ -42,26 +46,12 @@ using preview::detail::return_category; struct drop_niebloid { private: - // empty_view - 1 - template::value /* true */> - struct return_category_empty_view : std::true_type { - using category = return_category<1, decltype(preview_decay_copy(std::declval()))>; - }; - template - struct return_category_empty_view : std::false_type { - using category = return_category<0>; - }; - template - constexpr RT operator()(R&& r, D, return_category<1, RT>) const { - return std::forward(r); - } - - // subrange - 2 / 3 + // subrange template, random_access_range, sized_range>::value /* true */> struct return_category_subrange : std::true_type { using category = std::conditional_t::value, - return_category<2, T>, - return_category<3, T> + return_category<3, T>, + return_category<2, T> >; }; template @@ -69,117 +59,100 @@ struct drop_niebloid { using category = return_category<0>; }; - template - constexpr T operator()(R&& e, D f, return_category<2, T>) const { - auto inc = (std::min)(ranges::distance(e), f); - return T( - ranges::begin(e) + inc, - ranges::end(e), - preview::to_unsigned_like(ranges::distance(e) - inc) - ); - } - - // span - 3 + // span template - struct is_span : std::false_type {}; + struct return_category_span : std::false_type { + using category = return_category<0>; + }; template - struct is_span> : std::true_type {}; - - template::value /* true */> - struct return_category_span : std::true_type { - using category = return_category<3, span>; + struct return_category_span> : std::true_type { + using category = return_category<2, span>; }; - template - struct return_category_span : std::false_type { - using category = return_category<0>; +#if PREVIEW_CXX_VERSION >= 20 + template + struct return_category_span> : std::true_type { + using category = return_category<2, std::span>; }; +#endif - // basic_string_view - 3 - template + using is_basic_string_view = disjunction< is_specialization #if PREVIEW_CXX_VERSION >= 17 , is_specialization #endif - >::value /* true */> - struct return_category_string_view : std::true_type { - using category = return_category<3, T>; - }; - template - struct return_category_string_view : std::false_type { - using category = return_category<0>; - }; + >; - // iota_view - 3 - template, - sized_range, - random_access_range - >::value /* true */> - struct return_category_iota_view : std::true_type { - using category = return_category<3, T>; - }; - template - struct return_category_iota_view : std::false_type { - using category = return_category<0>; - }; + // 2.1 empty_view + template + constexpr auto call(R&& r, D, return_category<1>) const { + return preview_decay_copy(std::forward(r)); + } - // TODO: Investigate for subrange fallback + // 2.2 span, basic_string_view, iota_view, subrange (StoreSize == false) template - constexpr U operator()(R&& e, D f, return_category<3, U>) const { + constexpr U call(R&& e, D f, return_category<2, U>) const { return U( ranges::begin(e) + (std::min)(ranges::distance(e), f), ranges::end(e) ); } - // repeat_view - 4 - template::value /* false */> - struct return_category_repeat_view : std::false_type { - using category = return_category<0>; - }; - template - struct return_category_repeat_view : std::true_type { - using category = return_category<4, bool_constant::value>>; - }; + // 2.3 subrange + template + constexpr T call(R&& e, D f, return_category<3, T>) const { + auto inc = (std::min)(ranges::distance(e), f); + return T( + ranges::begin(e) + inc, + ranges::end(e), + preview::to_unsigned_like(ranges::distance(e) - inc) + ); + } + + // 2.4. repeat_view template - constexpr auto operator()(R&& e, D f, return_category<4, std::true_type /* sized_range */>) const { - return views::repeat(*e, ranges::distance(e) - (std::min)(ranges::distance(e), f)); + constexpr auto call(R&& e, D f, return_category<4, std::true_type /* sized_range */>) const { + return views::repeat( + preview::force_forward_like(*e.begin()), // *e.value_ + ranges::distance(e) - (std::min)(ranges::distance(e), f) + ); } template - constexpr auto operator()(R&& e, D, return_category<4, std::false_type /* sized_range */>) const { - return preview_decay_copy(e); + constexpr auto call(R&& e, D f, return_category<4, std::false_type /* sized_range */>) const { + return ((void)f, preview_decay_copy(e)); } - // drop_view - 0 (default) + // 2.5 drop_view template - constexpr drop_view> operator()(R&& r, D f, return_category<0>) const { + constexpr drop_view> call(R&& r, D f, return_category<5>) const { return drop_view>(std::forward(r), f); } - template - using category = - conditional_t< - return_category_empty_view, typename return_category_empty_view::category, // 1 - return_category_span, typename return_category_span::category, // 3 - return_category_string_view, typename return_category_string_view::category, // 3 - return_category_subrange, typename return_category_subrange::category, // 2 or 3 - return_category_iota_view, typename return_category_iota_view::category, // 3 - return_category_repeat_view, typename return_category_iota_view::category, // 4 - return_category<0> - >; + template + using category = conditional_t< + is_specialization, return_category<1>, + return_category_span, typename return_category_span::category, // 2 + is_basic_string_view, return_category<2, T>, + conjunction< + is_specialization, + sized_range, + random_access_range + >, return_category<2, T>, + return_category_subrange, typename return_category_subrange::category, // 2 or 3 + is_specialization, return_category<4, bool_constant::value>>, + return_category<5> + >; public: template::value, int> = 0> - constexpr auto - operator()(R&& r, range_difference_t count) const { + constexpr auto operator()(R&& r, range_difference_t count) const { using T = remove_cvref_t; - using D = range_difference_t; - return (*this)(std::forward(r), count, category{}); + return call(std::forward(r), std::move(count), category{}); } template constexpr auto operator()(DifferenceType&& count) const { - return range_adaptor>(std::forward(count)); + return range_adaptor>(std::forward(count)); } }; diff --git a/include/preview/__ranges/views/drop_view.h b/include/preview/__ranges/views/drop_view.h index 6af649f9..b8f5e229 100644 --- a/include/preview/__ranges/views/drop_view.h +++ b/include/preview/__ranges/views/drop_view.h @@ -137,7 +137,7 @@ class drop_view : public detail::drop_view_cached_begin, V> { } private: - V base_; + V base_{}; range_difference_t count_ = 0; }; diff --git a/include/preview/__ranges/views/drop_while_view.h b/include/preview/__ranges/views/drop_while_view.h index 91f95beb..7b99cd7d 100644 --- a/include/preview/__ranges/views/drop_while_view.h +++ b/include/preview/__ranges/views/drop_while_view.h @@ -30,7 +30,7 @@ namespace detail { template::value/* true */> struct drop_while_view_cached_begin : public view_interface { template - constexpr auto begin(Base& base, Pred& pred) { + constexpr auto begin_impl(Base& base, Pred& pred) { if (!cached_begin_.has_value()) { cached_begin_.emplace(ranges::find_if_not(base, std::cref(*pred))); } @@ -44,7 +44,7 @@ struct drop_while_view_cached_begin : public view_interface { template struct drop_while_view_cached_begin : public view_interface { template - constexpr auto begin(Base& base, Pred& pred) { + constexpr auto begin_impl(Base& base, Pred& pred) { return ranges::find_if_not(base, std::cref(*pred)); } }; @@ -56,6 +56,7 @@ class drop_while_view : public detail::drop_while_view_cached_begin, V> { using begin_base = detail::drop_while_view_cached_begin, V>; + using begin_base::begin_impl; public: static_assert(view::value, "Constraints not satisfied"); @@ -84,7 +85,7 @@ class drop_while_view } constexpr auto begin() { - return begin_base::begin(base_, pred_); + return begin_base::begin_impl(base_, pred_); } constexpr auto end() { diff --git a/include/preview/__ranges/views/enumerate_view.h b/include/preview/__ranges/views/enumerate_view.h index 59069dbf..f62a0027 100644 --- a/include/preview/__ranges/views/enumerate_view.h +++ b/include/preview/__ranges/views/enumerate_view.h @@ -364,7 +364,7 @@ class enumerate_view : public view_interface> { return sentinel(ranges::end(base_)); } - V base_; + V base_{}; }; #if __cplusplus >= 201703L diff --git a/include/preview/__ranges/views/filter_view.h b/include/preview/__ranges/views/filter_view.h index 3800122f..560e2344 100644 --- a/include/preview/__ranges/views/filter_view.h +++ b/include/preview/__ranges/views/filter_view.h @@ -14,6 +14,7 @@ #include "preview/__algorithm/ranges/find_if.h" #include "preview/__concepts/derived_from.h" #include "preview/__concepts/equality_comparable.h" +#include "preview/__core/std_version.h" #include "preview/__functional/invoke.h" #include "preview/__iterator/detail/have_cxx20_iterator.h" #include "preview/__iterator/indirect_unary_predicate.h" @@ -109,7 +110,7 @@ struct has_arrow template class filter_view : public view_interface>, detail::filter_view_cache { - V base_; + V base_{}; movable_box pred_; using cache_base = detail::filter_view_cache; @@ -279,7 +280,7 @@ class filter_view : public view_interface>, detail::filter_ } constexpr const Pred& pred() const { - assert(((void)"pred_ must contatin a value", pred_.has_value())); + assert(((void)"pred_ must contain a value", pred_.has_value())); return *pred_; } @@ -288,20 +289,20 @@ class filter_view : public view_interface>, detail::filter_ } constexpr auto end() { - return end(common_range{}); + return end_impl(common_range{}); } private: - constexpr iterator end(std::true_type /* common_range */) { + constexpr iterator end_impl(std::true_type /* common_range */) { return {*this, ranges::end(base_)}; } - constexpr sentinel end(std::false_type /* common_range */) { + constexpr sentinel end_impl(std::false_type /* common_range */) { return sentinel{*this}; } }; -#if __cplusplus >= 201703L +#if PREVIEW_CXX_VERSION >= 17 template filter_view(R&&, Pred) -> filter_view, Pred>; diff --git a/include/preview/__ranges/views/iota_view.h b/include/preview/__ranges/views/iota_view.h index a9180478..56e9bed4 100644 --- a/include/preview/__ranges/views/iota_view.h +++ b/include/preview/__ranges/views/iota_view.h @@ -378,18 +378,8 @@ class iota_view : public view_interface> { return iterator(value_); } - - template, - negation> - >::value, int> = 0> - constexpr sentinel end() const { - return sentinel(bound_); - } - template, - same_as - >::value, int> = 0> - constexpr iterator end() const { - return iterator(bound_); + constexpr auto end() const { + return end_impl(same_as{}, same_as{}); } constexpr bool empty() const { @@ -409,6 +399,19 @@ class iota_view : public view_interface> { } private: + template + constexpr iterator end_impl(std::true_type /* same_as */, Any) const { + return iterator{bound_}; + } + constexpr auto end_impl(std::false_type /* same_as */, + std::true_type /* same_as */) const { + return unreachable_sentinel; + } + constexpr sentinel end_impl(std::false_type /* same_as */, + std::false_type /* same_as */) const { + return sentinel{bound_}; + } + constexpr auto size_impl(std::true_type) const { return (value_ < 0) ? ((bound_ < 0) diff --git a/include/preview/__ranges/views/join_with_view.h b/include/preview/__ranges/views/join_with_view.h index 99a7e120..2cc2cdf1 100644 --- a/include/preview/__ranges/views/join_with_view.h +++ b/include/preview/__ranges/views/join_with_view.h @@ -602,7 +602,7 @@ class join_with_view : public detail::join_with_view_base{*this}; } - V base_; + V base_{}; Pattern pattern_; }; diff --git a/include/preview/__ranges/views/lazy_split_view.h b/include/preview/__ranges/views/lazy_split_view.h index 695f6ac4..101292af 100644 --- a/include/preview/__ranges/views/lazy_split_view.h +++ b/include/preview/__ranges/views/lazy_split_view.h @@ -81,13 +81,52 @@ class lazy_split_view template struct inner_iterator; + template::value> + struct outer_iterator_category { +#if !PREVIEW_STD_HAVE_CXX20_ITERATOR + using iterator_category = iterator_ignore; +#endif + }; + template + struct outer_iterator_category { + iterator_t current_{}; + using iterator_category = input_iterator_tag; + }; + + template + struct outer_iterator_current { + outer_iterator_current() = default; + template + outer_iterator_current(Unused&&) {} + }; + template + struct outer_iterator_current { + private: + using Base = maybe_const; + + public: + outer_iterator_current() = default; + explicit outer_iterator_current(iterator_t current) + : current_(std::move(current)) {} + + template = 0> + outer_iterator_current(outer_iterator_current& i) + : current_(std::move(i.current_)) {} + + iterator_t current_{}; + }; + template - struct outer_iterator { + struct outer_iterator + : outer_iterator_category> + , outer_iterator_current::value> + { private: + using current_base = outer_iterator_current::value>; + using Parent = maybe_const; using Base = maybe_const; Parent* parent_ = nullptr; - iterator_t current_{}; bool trailing_empty_ = false; friend struct outer_iterator; @@ -97,17 +136,34 @@ class lazy_split_view void increment_if_tiny(std::false_type, Last&&, Begin&&) noexcept {} template void increment_if_tiny(std::true_type, Last&& last, Begin&& begin) { - current_ = ranges::find(std::move(current_), last, *begin); - if (current_ != last) { - ++current_; - if (current_ == last) + current() = ranges::find(std::move(current()), last, *begin); + if (current() != last) { + ++current(); + if (current() == last) trailing_empty_ = true; } } + + constexpr outer_iterator post_increment(std::true_type) { + auto tmp = *this; + ++*this; + return tmp; + } + + constexpr void post_increment(std::false_type) { + ++*this; + } + + iterator_t& current_impl(std::true_type) { return current_base::current_; } + const iterator_t& current_impl(std::true_type) const { return current_base::current_; } + iterator_t& current_impl(std::false_type) { return *parent_->current_; } + const iterator_t& current_impl(std::false_type) const { return *parent_->current_; } + decltype(auto) current() { return current_impl(forward_range{}); } + decltype(auto) current() const { return current_impl(forward_range{}); } + public: - using iterator_concept = std::conditional_t< - forward_range::value, forward_iterator_tag, input_iterator_tag>; - using iterator_category = input_iterator_tag; + using iterator_concept = + std::conditional_t::value, forward_iterator_tag, input_iterator_tag>; struct value_type : view_interface { private: friend struct outer_iterator; @@ -141,17 +197,17 @@ class lazy_split_view template, forward_range >::value, int> = 0> - PREVIEW_CONSTEXPR_AFTER_CXX17 explicit outer_iterator(Parent& parent, iterator_t current) - : parent_(preview::addressof(parent)) - , current_(std::move(current)) {} + PREVIEW_CONSTEXPR_AFTER_CXX17 outer_iterator(Parent& parent, iterator_t current) + : current_base(std::move(current)) + , parent_(preview::addressof(parent)) {} template, convertible_to, iterator_t> >::value, int> = 0> constexpr outer_iterator(outer_iterator i) - : parent_(i.parent_) - , current_(std::move(i.current_)) + : current_base(i) + , parent_(i.parent_) , trailing_empty_(i.trailing_empty_) {} constexpr value_type operator*() const { @@ -159,53 +215,41 @@ class lazy_split_view } constexpr outer_iterator& operator++() { - const auto last = ranges::end(parent_->base_); - if (current_ == last) { + const auto end = ranges::end(parent_->base_); + if (current() == end) { trailing_empty_ = false; return *this; } - auto s = make_subrange(parent_->pattern_); - if (s.begin() == s.end()) { - ++current_; + auto p = make_subrange(parent_->pattern_); + if (p.begin() == p.end()) { + ++current(); } else if PREVIEW_CONSTEXPR_AFTER_CXX17 (detail::tiny_range::value) { - increment_if_tiny(detail::tiny_range{}, last, s.begin()); + increment_if_tiny(detail::tiny_range{}, end, p.begin()); } else { do { - auto b_p = ranges::mismatch(current_, last, s.begin(), s.end()); - if (b_p.in2 == s.end()) { - current_ = b_p.in1; - if (current_ == last) + auto b_p = ranges::mismatch(current(), end, p.begin(), p.end()); + if (b_p.in2 == p.end()) { + current() = b_p.in1; + if (current() == end) trailing_empty_ = true; break; } - } while (++current_ != last); + } while (++current() != end); } return *this; } - template, - forward_range - >::value, int> = 0> - constexpr outer_iterator operator++(int) { - auto tmp = *this; - ++*this; - return tmp; - } - - template, - negation> - >::value, int> = 0> - constexpr void operator++(int) { - ++*this; + constexpr decltype(auto) operator++(int) { + return post_increment(forward_range{}); } template, forward_range >::value, int> = 0> friend constexpr bool operator==(const outer_iterator& x, const outer_iterator& y) { - return x.current_ == y.current_ && x.trailing_empty_ == y.trailing_empty_; + return x.current() == y.current() && x.trailing_empty_ == y.trailing_empty_; } template, @@ -217,7 +261,7 @@ class lazy_split_view friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t) { using namespace rel_ops; - return x.current_ == ranges::end(x.parent_->base_) && !x.trailing_empty_; + return x.current() == ranges::end(x.parent_->base_) && !x.trailing_empty_; } friend constexpr bool operator==(default_sentinel_t, const outer_iterator& x) { @@ -259,7 +303,7 @@ class lazy_split_view bool incremented_ = false; constexpr bool equal_with(const inner_iterator& other) const { - return i_.current_ == other.i_.current_; + return i_.current() == other.i_.current(); } public: @@ -277,18 +321,18 @@ class lazy_split_view : i_(std::move(i)) {} constexpr const iterator_t& base() const & noexcept { - return i_.current_; + return i_.current(); } template, forward_range >::value, int> = 0> constexpr iterator_t base() && { - return std::move(i_.current_); + return std::move(i_.current()); } constexpr decltype(auto) operator*() const { - return *i_.current_; + return *i_.current(); } constexpr inner_iterator& operator++() { @@ -336,14 +380,18 @@ class lazy_split_view indirectly_swappable> >::value, int> = 0> friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y) - noexcept(noexcept(ranges::iter_swap(std::declval>(), std::declval>()))) + noexcept(noexcept(x.iter_swap_impl(y))) { x.iter_swap_impl(y); } + friend constexpr decltype(auto) iter_move(const inner_iterator& i) noexcept(noexcept(i.iter_move_impl())) { + return i.iter_move_impl(); + } + private: constexpr inner_iterator& pre_increment(std::true_type /* forward_range */) { - ++i_.current_; + ++i_.current(); return *this; } constexpr inner_iterator& pre_increment(std::false_type /* forward_range */) { @@ -366,32 +414,42 @@ class lazy_split_view } constexpr bool compare_with_default_sentinel(std::true_type /* tiny_range */) const { + using namespace preview::rel_ops; + auto p = preview::ranges::make_subrange(i_.parent_->pattern_); - auto last = ranges::end(i_.parent_->base_); + auto end = ranges::end(i_.parent_->base_); - const auto& cur = i_.current_; - if (cur == last) return true; + const auto& cur = i_.current(); + if (cur == end) return true; if (p.begin() == p.end()) return incremented_; return *cur == *p.begin(); } constexpr bool compare_with_default_sentinel(std::false_type /* tiny_range */) const { + using namespace preview::rel_ops; + auto p = preview::ranges::make_subrange(i_.parent_->pattern_); auto pcur = p.begin(); auto pend = p.end(); - auto last = ranges::end(i_.parent_->base_); + auto end = ranges::end(i_.parent_->base_); - auto cur = i_.current_; - if (cur == last) return true; + auto cur = i_.current(); + if (cur == end) return true; if (pcur == pend) return incremented_; do { if (!(*cur == *pcur)) return false; if (++pcur == pend) return true; - } while (!(++cur == last)); + } while (!(++cur == end)); return false; } - constexpr void iter_swap_impl(const inner_iterator& y) const { - ranges::iter_swap(i_.current_, y.i_.current_); + constexpr void iter_swap_impl(const inner_iterator& y) const + noexcept(noexcept(ranges::iter_swap(i_.current(), y.i_.current()))) + { + ranges::iter_swap(i_.current(), y.i_.current()); + } + + constexpr decltype(auto) iter_move_impl() const noexcept(noexcept(ranges::iter_move(i_.current()))) { + return ranges::iter_move(i_.current()); } }; @@ -418,8 +476,6 @@ class lazy_split_view : base_(views::all(std::forward(r))) , pattern_(views::single(std::move(e))) {} - - template, copy_constructible >::value, int> = 0> @@ -499,6 +555,7 @@ lazy_split_view(R&&, range_value_t) views::all_t>, single_view> >; + #endif } // namespace ranges diff --git a/include/preview/__ranges/views/repeat.h b/include/preview/__ranges/views/repeat.h index abf1d7ce..32fea70f 100644 --- a/include/preview/__ranges/views/repeat.h +++ b/include/preview/__ranges/views/repeat.h @@ -13,6 +13,7 @@ #include "preview/__concepts/same_as.h" #include "preview/__concepts/semiregular.h" #include "preview/__core/inline_variable.h" +#include "preview/__ranges/range_adaptor.h" #include "preview/__ranges/views/repeat_view.h" #include "preview/__type_traits/conjunction.h" #include "preview/__type_traits/disjunction.h" @@ -25,19 +26,25 @@ namespace detail { struct repeat_niebloid { template >, - std::is_object< remove_cvref_t > + move_constructible< std::decay_t >, + std::is_object < std::decay_t >, + same_as < std::decay_t, + std::remove_cv_t> > >::value, int> = 0> constexpr auto operator()(W&& value) const { return repeat_view>{std::forward(value)}; } template >, - std::is_object< remove_cvref_t >, + move_constructible< std::decay_t >, + semiregular < std::decay_t >, + + std::is_object < std::decay_t >, + same_as < std::decay_t, + std::remove_cv_t> >, disjunction< - integer_like< remove_cvref_t >, - same_as< remove_cvref_t, unreachable_sentinel_t > + ranges::detail::integer_like_with_usable_difference_type< std::decay_t >, + same_as< std::decay_t, unreachable_sentinel_t > > >::value, int> = 0> constexpr auto operator()(W&& value, Bound&& bound) const { diff --git a/include/preview/__ranges/views/repeat_view.h b/include/preview/__ranges/views/repeat_view.h index 3c7c46df..85f33d2e 100644 --- a/include/preview/__ranges/views/repeat_view.h +++ b/include/preview/__ranges/views/repeat_view.h @@ -11,6 +11,7 @@ #include #include "preview/__concepts/constructible_from.h" +#include "preview/__concepts/copy_constructible.h" #include "preview/__concepts/integer_like.h" #include "preview/__concepts/move_constructible.h" #include "preview/__concepts/same_as.h" @@ -165,6 +166,9 @@ class repeat_view : public view_interface> { repeat_view() : value_(W()), bound_() {} + template, + copy_constructible + >::value, int> = 0> constexpr explicit repeat_view(const W& value, Bound bound = Bound()) : value_(value), bound_(bound){} diff --git a/include/preview/__ranges/views/single.h b/include/preview/__ranges/views/single.h index 94130f58..55290e3d 100644 --- a/include/preview/__ranges/views/single.h +++ b/include/preview/__ranges/views/single.h @@ -11,8 +11,10 @@ #include "preview/__core/inline_variable.h" #include "preview/__concepts/constructible_from.h" #include "preview/__concepts/copy_constructible.h" +#include "preview/__concepts/move_constructible.h" #include "preview/__ranges/movable_box.h" #include "preview/__ranges/view_interface.h" +#include "preview/__type_traits/conjunction.h" #include "preview/__utility/in_place.h" namespace preview { @@ -21,11 +23,14 @@ namespace ranges { template class single_view : public ranges::view_interface> { public: - static_assert(copy_constructible::value, "Constraints not satisfied"); + static_assert(move_constructible::value, "Constraints not satisfied"); static_assert(std::is_object::value, "Constraints not satisfied"); constexpr single_view() = default; + template, + copy_constructible + >::value, int> = 0> constexpr explicit single_view(const T& t) : value_(t) {} diff --git a/include/preview/__ranges/views/split_view.h b/include/preview/__ranges/views/split_view.h index d357fecb..9f5a2b1e 100644 --- a/include/preview/__ranges/views/split_view.h +++ b/include/preview/__ranges/views/split_view.h @@ -137,8 +137,7 @@ class split_view : public view_interface> { : end_(ranges::end(parent.base_)) {} friend constexpr bool operator==(const iterator& x, const sentinel& y) { - using namespace preview::rel_ops; - return x.cur_ == y.end_ && !x.trailing_empty_; + return y.compare_with_iterator(x); } friend constexpr bool operator!=(const iterator& x, const sentinel& y) { @@ -154,13 +153,18 @@ class split_view : public view_interface> { } private: + constexpr bool compare_with_iterator(const iterator& x) const { + using namespace preview::rel_ops; + return x.cur_ == end_ && !x.trailing_empty_; + } sentinel_t end_ = sentinel_t(); }; split_view() = default; constexpr explicit split_view(V base, Pattern pattern) - : base_(std::move(base)), pattern_(std::move(pattern)) {} + : base_(std::move(base)) + , pattern_(std::move(pattern)) {} template::value, int> = 0> constexpr explicit split_view(R&& r, range_value_t e) @@ -186,14 +190,14 @@ class split_view : public view_interface> { } constexpr auto end() { - return end(common_range{}); + return end_impl(common_range{}); } private: - constexpr iterator end(std::true_type /* common_range */) { + constexpr iterator end_impl(std::true_type /* common_range */) { return iterator{*this, ranges::end(base_), {}}; } - constexpr sentinel end(std::false_type /* common_range */) { + constexpr sentinel end_impl(std::false_type /* common_range */) { return sentinel{*this}; } diff --git a/include/preview/__ranges/views/take.h b/include/preview/__ranges/views/take.h index a33bcdf2..0398cd3e 100644 --- a/include/preview/__ranges/views/take.h +++ b/include/preview/__ranges/views/take.h @@ -33,6 +33,7 @@ #include "preview/__type_traits/negation.h" #include "preview/__type_traits/remove_cvref.h" #include "preview/__type_traits/type_identity.h" +#include "preview/__utility/forward_like.h" #if PREVIEW_CXX_VERSION >= 17 #include @@ -50,30 +51,23 @@ using preview::detail::return_category; struct take_niebloid { private: - template::value /* true */> - struct return_category_empty_view : std::true_type { - using category = return_category<1, decltype(preview_decay_copy(std::declval()))>; - }; - template - struct return_category_empty_view : std::false_type { - using category = return_category<0>; - }; - template - constexpr RT operator()(R&& r, range_difference_t count, return_category<1, RT>) const { + // (1) empty_view + template + constexpr auto call(R&& r, range_difference_t count, return_category<1>) const { return ((void)count, preview_decay_copy(std::forward(r))); } - template + template struct return_category_span : std::false_type { using category = return_category<0>; }; - template - struct return_category_span, D> : std::true_type { + template + struct return_category_span> : std::true_type { using category = return_category<2, span>; }; #if PREVIEW_CXX_VERSION >= 20 - template - struct return_category_span, D> : std::true_type { + template + struct return_category_span> : std::true_type { using category = return_category<2, std::span>; }; #endif @@ -101,81 +95,75 @@ struct take_niebloid { using category = return_category<0>; }; + // (2) span, string_view, subrange template - constexpr U operator()(R&& e, range_difference_t f, return_category<2, U>) const { + constexpr U call(R&& e, range_difference_t f, return_category<2, U>) const { using D = range_difference_t; - return U( - ranges::begin(e), - ranges::begin(e) + (std::min)(ranges::distance(e), f) - ); + auto size = (std::min)(ranges::distance(e), f); + auto first = ranges::begin(e); + return U(first, first + size); } - template, random_access_range, sized_range>::value /* true */> - struct return_category_iota_view : std::true_type { - using category = return_category<3, T>; - }; - template - struct return_category_iota_view : std::false_type { - using category = return_category<0>; - }; - template - constexpr auto operator()(R&& e, ranges::range_difference_t f, return_category<3, T /* unused */>) const { + // (3) iota_view + template + constexpr auto call(R&& e, ranges::range_difference_t f, return_category<3>) const { using D = ranges::range_difference_t; - return views::iota( - *ranges::begin(e), - *(ranges::begin(e) + (std::min)(ranges::distance(e), f)) - ); + auto size = (std::min)(ranges::distance(e), f); + auto first = ranges::begin(e); + return views::iota(*first, *(first + size)); } - template::value /* true */ > - struct return_category_repeat_view : std::true_type { - using category = return_category<4, bool_constant::value>>; - }; - template - struct return_category_repeat_view : std::false_type { - using category = return_category<0>; - }; + // (4) repeat_view template - constexpr auto operator()(R&& e, ranges::range_difference_t f, return_category<4, std::true_type /* sized_range */>) const { + constexpr auto call(R&& e, ranges::range_difference_t f, return_category<4, std::true_type /* sized_range */>) const { using D = ranges::range_difference_t; - return views::repeat(*(e.begin()), (std::min)(ranges::distance(e), f)); + return views::repeat( + preview::force_forward_like(*e.begin()), // *e.value_ + (std::min)(ranges::distance(e), f) + ); } template - constexpr auto operator()(R&& e, ranges::range_difference_t f, return_category<4, std::false_type /* sized_range */>) const { + constexpr auto call(R&& e, ranges::range_difference_t f, return_category<4, std::false_type /* sized_range */>) const { using D = ranges::range_difference_t; - return views::repeat(*(e.begin()), static_cast(f)); + return views::repeat( + preview::force_forward_like(*e.begin()), // *e.value_ + static_cast(f) + ); } - template - constexpr TakeView operator()(R&& r, ranges::range_difference_t f, return_category<5, TakeView>) const { - return TakeView(std::forward(r), f); + // (5) others + template + constexpr auto call(R&& r, ranges::range_difference_t f, return_category<5>) const { + return take_view>(std::forward(r), f); } - template + // TODO: Check std::ranges too + template using category = conditional_t< - return_category_empty_view, typename return_category_empty_view::category, // 1 - return_category_span, typename return_category_span::category, // 2 - return_category_string_view, typename return_category_string_view::category, // 2 - return_category_subrange, typename return_category_subrange::category, // 2 - return_category_iota_view, typename return_category_iota_view::category, // 3 - return_category_repeat_view, typename return_category_repeat_view::category, // 4 - return_category<5, take_view>> // 5 + is_specialization, return_category<1>, // 1 + return_category_span, typename return_category_span ::category, // 2 + return_category_string_view, typename return_category_string_view::category, // 2 + return_category_subrange, typename return_category_subrange ::category, // 2 + conjunction< + is_specialization, + random_access_range, + sized_range + >, return_category<3>, // 3 + is_specialization, return_category<4, bool_constant::value>>, // 4 + return_category<5> // 5 >; public: template::value, int> = 0> - constexpr auto - operator()(R&& r, ranges::range_difference_t count) const { + constexpr auto operator()(R&& r, ranges::range_difference_t count) const { using T = remove_cvref_t; - using D = ranges::range_difference_t; - static_assert(convertible_to::value, "Constraints not satisfied"); - return (*this)(std::forward(r), count, category{}); + return call(std::forward(r), std::move(count), category{}); } template constexpr auto operator()(DifferenceType&& count) const { - return range_adaptor>(std::forward(count)); + return range_adaptor>(std::forward(count)); } }; } // namespace detail diff --git a/include/preview/__ranges/views/take_view.h b/include/preview/__ranges/views/take_view.h index 2e8a4815..2428379f 100644 --- a/include/preview/__ranges/views/take_view.h +++ b/include/preview/__ranges/views/take_view.h @@ -134,7 +134,9 @@ class take_view : public view_interface> { : base_(std::move(base)), count_(count) {} - template::value, int> = 0> + template, + copy_constructible + >::value, int> = 0> constexpr V base() const& { return base_; } @@ -144,129 +146,133 @@ class take_view : public view_interface> { } - template >, - sized_range, - random_access_range + template, + negation< simple_view >, + sized_range, + random_access_range >::value, int> = 0> constexpr auto begin() { return ranges::begin(base_); } - template >, - sized_range, - negation< random_access_range > + template, + negation< simple_view >, + sized_range, + negation< random_access_range > >::value, int> = 0> constexpr auto begin() { - using I = remove_cvref_t>; - return counted_iterator(ranges::begin(base_), ranges::range_difference_t(this->size())); + using I = remove_cvref_t>; + return counted_iterator(ranges::begin(base_), ranges::range_difference_t(this->size())); } - template >, - negation< sized_range > + template, + negation< simple_view >, + negation< sized_range > >::value, int> = 0> constexpr auto begin() { - using I = remove_cvref_t>; + using I = remove_cvref_t>; return counted_iterator(ranges::begin(base_), count_); } - template, - sized_range, - random_access_range + template, + range, + sized_range, + random_access_range >::value, int> = 0> constexpr auto begin() const { return ranges::begin(base_); } - template, - sized_range, - negation< random_access_range > + template, + range, + sized_range, + negation< random_access_range > >::value, int> = 0> constexpr auto begin() const { - using I = remove_cvref_t>; - return counted_iterator(ranges::begin(base_), ranges::range_difference_t(this->size())); + using I = remove_cvref_t>; + return counted_iterator(ranges::begin(base_), ranges::range_difference_t(this->size())); } - template, - negation< sized_range > + template, + range, + negation< sized_range > >::value, int> = 0> constexpr auto begin() const { - using I = remove_cvref_t>; + using I = remove_cvref_t>; return counted_iterator(ranges::begin(base_), count_); } - template >, - sized_range, - random_access_range + template, + negation< simple_view >, + sized_range, + random_access_range >::value, int> = 0> constexpr auto end() { - return ranges::begin(base_) + ranges::range_difference_t(this->size()); + return ranges::begin(base_) + ranges::range_difference_t(this->size()); } - template >, - sized_range, - negation< random_access_range > + template, + negation< simple_view >, + sized_range, + negation< random_access_range > >::value, int> = 0> constexpr default_sentinel_t end() { return default_sentinel; } - template >, - negation< sized_range > + template, + negation< simple_view >, + negation< sized_range > >::value, int> = 0> constexpr sentinel end() { return sentinel(ranges::end(base_)); } - template, - sized_range, - random_access_range + template, + range, + sized_range, + random_access_range >::value, int> = 0> constexpr auto end() const { - return ranges::begin(base_) + ranges::range_difference_t(this->size()); + return ranges::begin(base_) + ranges::range_difference_t(this->size()); } - template, - sized_range, - negation< random_access_range > + template, + range, + sized_range, + negation< random_access_range > >::value, int> = 0> constexpr default_sentinel_t end() const { return default_sentinel; } - template, - negation< sized_range > + template, + range, + negation< sized_range > >::value, int> = 0> constexpr sentinel end() const { return sentinel(ranges::end(base_)); } - template::value, int> = 0> + template, + ranges::sized_range + >::value, int> = 0> constexpr auto size() { auto n = ranges::size(base_); return (std::min)(n, static_cast(count_)); } - template::value, int> = 0> + template, + ranges::sized_range + >::value, int> = 0> constexpr auto size() const { auto n = ranges::size(base_); return (std::min)(n, static_cast(count_)); } private: - V base_; + V base_{}; range_difference_t count_ = 0; }; diff --git a/include/preview/__ranges/views/take_while.h b/include/preview/__ranges/views/take_while.h index b4d74dbe..7c765b1a 100644 --- a/include/preview/__ranges/views/take_while.h +++ b/include/preview/__ranges/views/take_while.h @@ -27,17 +27,19 @@ namespace detail { struct take_while_niebloid { template>, - view>, - input_range>, - std::is_object>, - indirect_unary_predicate, iterator_t>> + has_typename_type < detail::all_t_impl >, + view < all_t >, + input_range < all_t >, + std::is_object < std::decay_t >, + indirect_unary_predicate, iterator_t>> >::value, int> = 0> constexpr auto operator()(R&& r, Pred&& pred) const { return take_while_view, std::decay_t>(std::forward(r), std::forward(pred)); } - template + template> + ::value, int> = 0> constexpr auto operator()(Pred&& pred) const { return range_adaptor>(std::forward(pred)); } diff --git a/include/preview/__ranges/views/transform_view.h b/include/preview/__ranges/views/transform_view.h index fb43ea88..4f155107 100644 --- a/include/preview/__ranges/views/transform_view.h +++ b/include/preview/__ranges/views/transform_view.h @@ -325,9 +325,10 @@ class transform_view : public view_interface> { return iterator{*this, ranges::begin(base_)}; } - template, - regular_invocable> + template, std::is_same, + range, + regular_invocable> >::value, int> = 0> constexpr iterator begin() const { return iterator{*this, ranges::begin(base_)}; @@ -347,18 +348,18 @@ class transform_view : public view_interface> { return iterator{*this, ranges::end(base_)}; } - template, - negation>, - regular_invocable> + template, std::is_same, + range, + negation>, + regular_invocable> >::value, int> = 0> constexpr sentinel end() const { return sentinel{ranges::end(base_)}; } - template, - regular_invocable> + template, std::is_same, + common_range, + regular_invocable> >::value, int> = 0> constexpr iterator end() const { return iterator{*this, ranges::end(base_)}; diff --git a/include/preview/__ranges/views/zip_transform.h b/include/preview/__ranges/views/zip_transform.h index 5e2d186d..dbe291fc 100644 --- a/include/preview/__ranges/views/zip_transform.h +++ b/include/preview/__ranges/views/zip_transform.h @@ -8,6 +8,7 @@ #include #include "preview/__concepts/copy_constructible.h" +#include "preview/__concepts/move_constructible.h" #include "preview/__concepts/invocable.h" #include "preview/__core/decay_copy.h" #include "preview/__core/inline_variable.h" @@ -25,26 +26,25 @@ namespace views { namespace detail { struct zip_transform_niebloid { - private: - template, is_referencable>::value /* false */> - struct check : std::false_type {}; - - template - struct check - : conjunction< - regular_invocable, - std::is_object> - > {}; - - public: - template>::value, int> = 0> + template >, + regular_invocable < std::decay_t& >, + std::is_object < invoke_result_t&> > + >::value, int> = 0> constexpr auto operator()(F&& f) const { - using FD = std::decay_t; + using FD = std::decay_t; return ((void)f, preview_decay_copy(views::empty>>)); } - template + template >, + input_range < views::all_t >..., + view < views::all_t >..., + std::is_object < std::decay_t >, + regular_invocable < std::decay_t&, range_reference_t>... >, + is_referencable < invoke_result_t>...> > + >::value, int> = 0> constexpr auto operator()(F&& f, Rs&&... rs) const { return zip_transform_view, views::all_t...>(std::forward(f), std::forward(rs)...); } diff --git a/include/preview/__type_traits/override_cvref.h b/include/preview/__type_traits/override_cvref.h index 88b51d75..4e2b27e3 100644 --- a/include/preview/__type_traits/override_cvref.h +++ b/include/preview/__type_traits/override_cvref.h @@ -30,7 +30,16 @@ template using override_reference_t = typename override_reference::type; template -using override_cvref_t = override_reference_t>; +using override_const_t = copy_const_t>; + +template +using override_volatile_t = copy_volatile_t>; + +template +using override_cv_t = override_const_t>; + +template +using override_cvref_t = override_reference_t>; } // namespace preview diff --git a/include/preview/__utility/forward_like.h b/include/preview/__utility/forward_like.h index 5c71df1f..7d3261d5 100644 --- a/include/preview/__utility/forward_like.h +++ b/include/preview/__utility/forward_like.h @@ -8,19 +8,28 @@ #include #include "preview/__type_traits/copy_cvref.h" +#include "preview/__type_traits/override_cvref.h" namespace preview { template constexpr auto&& forward_like(U&& x) noexcept { using CU = copy_const_t, std::remove_reference_t>; - using RU = std::conditional_t::value, std::remove_reference_t&&, CU&>; + using RU = override_reference_t; return static_cast(x); } template using forward_like_t = decltype(preview::forward_like(std::declval())); +// Same with `forward_like`, but it can remove const qualifier. +template +constexpr auto&& force_forward_like(U&& x) noexcept { + using CU = override_const_t, std::remove_reference_t>; + using RU = override_reference_t; + return static_cast(const_cast>(x)); +} + } // namespace preview #endif // PREVIEW_UTILITY_FORWARD_LIKE_H_ diff --git a/test/ranges_views.cc b/test/ranges_views.cc index 87c3b6cb..eaf56a83 100644 --- a/test/ranges_views.cc +++ b/test/ranges_views.cc @@ -623,6 +623,14 @@ TEST(VERSIONED(RangesViews), drop_view) { #endif } + { // repeat_view + auto d = views::repeat(std::string("hello"), 10) | views::drop(8); + auto it = d.begin(); + EXPECT_EQ(*it++, "hello"s); + EXPECT_EQ(*it++, "hello"s); + EXPECT_EQ(it, d.end()); + } + auto ints = views::iota(0) | views::take(10); #if PREVIEW_CXX_VERSION >= 17 for (auto i : ints | views::drop(5)) { (void)i; } diff --git a/test/utility.cc b/test/utility.cc index 57fdd30e..899eca0f 100644 --- a/test/utility.cc +++ b/test/utility.cc @@ -1,6 +1,103 @@ #include "preview/utility.h" #include "gtest.h" +#include +#include -// TODO: Write test -TEST(VERSIONED(Utility), basics) {} +#include "preview/optional.h" + +static constexpr int kTypeLvalueReference = 0x0; +static constexpr int kTypeRvalueReference = 0x1; +static constexpr int kTypeConst = 0x2; + +struct TypeTeller +{ + int operator()() const & { return kTypeLvalueReference | kTypeConst; } + int operator()() & { return kTypeLvalueReference; } + int operator()() const && { return kTypeRvalueReference | kTypeConst; } + int operator()() && { return kTypeRvalueReference; } +}; + +struct FarStates { + std::unique_ptr ptr; + preview::optional opt; + std::vector container; + + template + static auto&& from_ptr_impl(Self&& self) { + if (!self.ptr) + throw preview::bad_optional_access{}; + return preview::forward_like(*self.ptr); + // It is not good to use *std::forward(self).ptr, because + // std::unique_ptr always dereferences to a non-const lvalue. + } + + template + static auto&& from_index_impl(Self&& self, std::size_t i) { + return preview::forward_like(self.container.at(i)); + // It is not so good to use std::forward(self)[i], because + // containers do not provide rvalue subscript access, although they could. + } + + template + static auto&& from_opt_impl(Self&& self) { + return preview::forward_like(self.opt.value()); + // It is OK to use std::forward(self).opt.value(), + // because preview::optional provides suitable accessors. + } + + auto&& from_opt() const & { return from_opt_impl(*this); } + auto&& from_opt() & { return from_opt_impl(*this); } + auto&& from_opt() const && { return from_opt_impl(std::move(*this)); } + auto&& from_opt() && { return from_opt_impl(std::move(*this)); } + + auto&& operator[](std::size_t i) const & { return from_index_impl(*this, i); } + auto&& operator[](std::size_t i) & { return from_index_impl(*this, i); } + auto&& operator[](std::size_t i) const && { return from_index_impl(std::move(*this), i); } + auto&& operator[](std::size_t i) && { return from_index_impl(std::move(*this), i); } + + auto&& from_ptr() const & { return from_ptr_impl(*this); } + auto&& from_ptr() & { return from_ptr_impl(*this); } + auto&& from_ptr() const && { return from_ptr_impl(std::move(*this)); } + auto&& from_ptr() && { return from_ptr_impl(std::move(*this)); } +}; + +TEST(VERSIONED(Utility), forward_like) { + FarStates my_state; + my_state.ptr = std::make_unique(); + my_state.opt.emplace(TypeTeller{}); + my_state.container = std::vector(1); + + EXPECT_EQ(my_state.from_ptr()(), kTypeLvalueReference); + EXPECT_EQ(my_state.from_opt()(), kTypeLvalueReference); + EXPECT_EQ(my_state[0](), kTypeLvalueReference); + + EXPECT_EQ(preview::as_const(my_state).from_ptr()(), kTypeLvalueReference | kTypeConst); + EXPECT_EQ(preview::as_const(my_state).from_opt()(), kTypeLvalueReference | kTypeConst); + EXPECT_EQ(preview::as_const(my_state)[0](), kTypeLvalueReference | kTypeConst); + + EXPECT_EQ(std::move(my_state).from_ptr()(), kTypeRvalueReference); + EXPECT_EQ(std::move(my_state).from_opt()(), kTypeRvalueReference); + EXPECT_EQ(std::move(my_state)[0](), kTypeRvalueReference); + + EXPECT_EQ(std::move(preview::as_const(my_state)).from_ptr()(), kTypeRvalueReference | kTypeConst); + EXPECT_EQ(std::move(preview::as_const(my_state)).from_opt()(), kTypeRvalueReference | kTypeConst); + EXPECT_EQ(std::move(preview::as_const(my_state))[0](), kTypeRvalueReference | kTypeConst); +} + +TEST(VERSIONED(Utility), force_forward_like) { + FarStates my_state; + my_state.ptr = std::make_unique(); + my_state.opt.emplace(TypeTeller{}); + my_state.container = std::vector(1); + + const auto& cref_my_state = my_state; + + EXPECT_EQ(cref_my_state.from_ptr()(), kTypeLvalueReference | kTypeConst); + EXPECT_EQ(cref_my_state.from_opt()(), kTypeLvalueReference | kTypeConst); + EXPECT_EQ(cref_my_state[0](), kTypeLvalueReference | kTypeConst); + + EXPECT_EQ(preview::force_forward_like(cref_my_state.from_ptr())(), kTypeLvalueReference); + EXPECT_EQ(preview::force_forward_like(cref_my_state.from_opt())(), kTypeLvalueReference); + EXPECT_EQ(preview::force_forward_like(cref_my_state[0])(), kTypeLvalueReference); +}