1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP___RANGES_NON_PROPAGATING_CACHE_H
11#define _LIBCPP___RANGES_NON_PROPAGATING_CACHE_H
12
13#include <__config>
14#include <__iterator/concepts.h> // indirectly_readable
15#include <__iterator/iterator_traits.h> // iter_reference_t
16#include <__memory/addressof.h>
17#include <__utility/forward.h>
18#include <optional>
19
20#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
21# pragma GCC system_header
22#endif
23
24_LIBCPP_BEGIN_NAMESPACE_STD
25
26#if _LIBCPP_STD_VER >= 20
27
28namespace ranges {
29// __non_propagating_cache is a helper type that allows storing an optional value in it,
30// but which does not copy the source's value when it is copy constructed/assigned to,
31// and which resets the source's value when it is moved-from.
32//
33// This type is used as an implementation detail of some views that need to cache the
34// result of `begin()` in order to provide an amortized O(1) begin() method. Typically,
35// we don't want to propagate the value of the cache upon copy because the cached iterator
36// may refer to internal details of the source view.
37template <class _Tp>
38 requires is_object_v<_Tp>
39class _LIBCPP_TEMPLATE_VIS __non_propagating_cache {
40 struct __from_tag {};
41 struct __forward_tag {};
42
43 // This helper class is needed to perform copy and move elision when
44 // constructing the contained type from an iterator.
45 struct __wrapper {
46 template <class... _Args>
47 _LIBCPP_HIDE_FROM_ABI constexpr explicit __wrapper(__forward_tag, _Args&&... __args)
48 : __t_(std::forward<_Args>(__args)...) {}
49 template <class _Fn>
50 _LIBCPP_HIDE_FROM_ABI constexpr explicit __wrapper(__from_tag, _Fn const& __f) : __t_(__f()) {}
51 _Tp __t_;
52 };
53
54 optional<__wrapper> __value_ = nullopt;
55
56public:
57 _LIBCPP_HIDE_FROM_ABI __non_propagating_cache() = default;
58
59 _LIBCPP_HIDE_FROM_ABI constexpr __non_propagating_cache(__non_propagating_cache const&) noexcept
60 : __value_(nullopt) {}
61
62 _LIBCPP_HIDE_FROM_ABI constexpr __non_propagating_cache(__non_propagating_cache&& __other) noexcept
63 : __value_(nullopt) {
64 __other.__value_.reset();
65 }
66
67 _LIBCPP_HIDE_FROM_ABI constexpr __non_propagating_cache& operator=(__non_propagating_cache const& __other) noexcept {
68 if (this != std::addressof(__other)) {
69 __value_.reset();
70 }
71 return *this;
72 }
73
74 _LIBCPP_HIDE_FROM_ABI constexpr __non_propagating_cache& operator=(__non_propagating_cache&& __other) noexcept {
75 __value_.reset();
76 __other.__value_.reset();
77 return *this;
78 }
79
80 _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() { return __value_->__t_; }
81 _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const { return __value_->__t_; }
82
83 _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const { return __value_.has_value(); }
84
85 template <class _Fn>
86 _LIBCPP_HIDE_FROM_ABI constexpr _Tp& __emplace_from(_Fn const& __f) {
87 return __value_.emplace(__from_tag{}, __f).__t_;
88 }
89
90 template <class... _Args>
91 _LIBCPP_HIDE_FROM_ABI constexpr _Tp& __emplace(_Args&&... __args) {
92 return __value_.emplace(__forward_tag{}, std::forward<_Args>(__args)...).__t_;
93 }
94};
95
96struct __empty_cache {};
97} // namespace ranges
98
99#endif // _LIBCPP_STD_VER >= 20
100
101_LIBCPP_END_NAMESPACE_STD
102
103#endif // _LIBCPP___RANGES_NON_PROPAGATING_CACHE_H
104