auto __tmp = ranges::next(ranges::begin(__base_), __count_, ranges::end(__base_));
if constexpr (_UseCache)
- __cached_begin_.__set(__tmp);
+ __cached_begin_.__emplace(__tmp);
return __tmp;
}
if constexpr (__ref_is_glvalue)
return *__outer_;
else
- return __parent_->__cache_.__emplace_deref(__outer_);
+ return __parent_->__cache_.__emplace_from([&]() -> decltype(auto) { return *__outer_; });
}();
__inner_ = ranges::begin(__inner);
if (*__inner_ != ranges::end(__inner))
#include <__iterator/concepts.h> // indirectly_readable
#include <__iterator/iterator_traits.h> // iter_reference_t
#include <__memory/addressof.h>
+#include <__utility/forward.h>
#include <concepts> // constructible_from
#include <optional>
#include <type_traits>
#pragma GCC system_header
#endif
-_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
-// clang-format off
-
#if !defined(_LIBCPP_HAS_NO_RANGES)
namespace ranges {
template<class _Tp>
requires is_object_v<_Tp>
class _LIBCPP_TEMPLATE_VIS __non_propagating_cache {
- optional<_Tp> __value_ = nullopt;
+ struct __from_tag { };
+ struct __forward_tag { };
+
+ // This helper class is needed to perform copy and move elision when
+ // constructing the contained type from an iterator.
+ struct __wrapper {
+ template<class ..._Args>
+ constexpr explicit __wrapper(__forward_tag, _Args&& ...__args) : __t_(_VSTD::forward<_Args>(__args)...) { }
+ template<class _Fn>
+ constexpr explicit __wrapper(__from_tag, _Fn const& __f) : __t_(__f()) { }
+ _Tp __t_;
+ };
+
+ optional<__wrapper> __value_ = nullopt;
public:
_LIBCPP_HIDE_FROM_ABI __non_propagating_cache() = default;
}
_LIBCPP_HIDE_FROM_ABI
- constexpr _Tp& operator*() { return *__value_; }
+ constexpr _Tp& operator*() { return __value_->__t_; }
_LIBCPP_HIDE_FROM_ABI
- constexpr _Tp const& operator*() const { return *__value_; }
+ constexpr _Tp const& operator*() const { return __value_->__t_; }
_LIBCPP_HIDE_FROM_ABI
constexpr bool __has_value() const { return __value_.has_value(); }
+
+ template<class _Fn>
_LIBCPP_HIDE_FROM_ABI
- constexpr void __set(_Tp const& __value) { __value_.emplace(__value); }
- _LIBCPP_HIDE_FROM_ABI
- constexpr void __set(_Tp&& __value) { __value_.emplace(_VSTD::move(__value)); }
+ constexpr _Tp& __emplace_from(_Fn const& __f) {
+ return __value_.emplace(__from_tag{}, __f).__t_;
+ }
- template<class _Other>
+ template<class ..._Args>
_LIBCPP_HIDE_FROM_ABI
- constexpr _Tp& __emplace_deref(const _Other& __value) {
- __value_.reset();
- __value_.emplace(*__value);
- return *__value_;
+ constexpr _Tp& __emplace(_Args&& ...__args) {
+ return __value_.emplace(__forward_tag{}, _VSTD::forward<_Args>(__args)...).__t_;
}
};
_LIBCPP_END_NAMESPACE_STD
-_LIBCPP_POP_MACROS
-
#endif // _LIBCPP___RANGES_NON_PROPAGATING_CACHE_H
auto __tmp = _VSTD::make_reverse_iterator(ranges::next(ranges::begin(__base_), ranges::end(__base_)));
if constexpr (_UseCache)
- __cached_begin_.__set(__tmp);
+ __cached_begin_.__emplace(__tmp);
return __tmp;
}
// Assign to an empty cache
{
- Cache a; a.__set(T{3});
+ Cache a; a.__emplace(3);
Cache b;
Cache& result = (b = a);
// Assign to a non-empty cache
{
- Cache a; a.__set(T{3});
- Cache b; b.__set(T{5});
+ Cache a; a.__emplace(3);
+ Cache b; b.__emplace(5);
Cache& result = (b = a);
assert(&result == &b);
// Self-assignment should not do anything (case with non-empty cache)
{
- Cache b; b.__set(T{5});
+ Cache b; b.__emplace(5);
Cache& result = (b = b);
assert(&result == &b);
assert(b.__has_value());
// Assign to an empty cache
{
- Cache a; a.__set(T{3});
+ Cache a; a.__emplace(3);
Cache b;
Cache& result = (b = std::move(a));
// Assign to a non-empty cache
{
- Cache a; a.__set(T{3});
- Cache b; b.__set(T{5});
+ Cache a; a.__emplace(3);
+ Cache b; b.__emplace(5);
Cache& result = (b = std::move(a));
assert(&result == &b);
// Self-assignment should clear the cache (case with non-empty cache)
{
- Cache b; b.__set(T{5});
+ Cache b; b.__emplace(5);
Cache& result = (b = std::move(b));
assert(&result == &b);
using Cache = std::ranges::__non_propagating_cache<T>;
static_assert(std::is_nothrow_copy_constructible_v<Cache>);
Cache a;
- a.__set(T{3});
+ a.__emplace(3);
// Test with direct initialization
{
// Test with direct initialization
{
Cache a;
- a.__set(T{3});
+ a.__emplace(3);
Cache b(std::move(a));
assert(!b.__has_value()); // make sure we don't propagate
// Test with copy initialization
{
Cache a;
- a.__set(T{3});
+ a.__emplace(3);
Cache b = std::move(a);
assert(!b.__has_value()); // make sure we don't propagate
// non-const version
{
- Cache cache; cache.__set(T{3});
+ Cache cache; cache.__emplace(3);
T& result = *cache;
assert(result == T{3});
}
// const version
{
- Cache cache; cache.__set(T{3});
+ Cache cache; cache.__emplace(3);
T const& result = *static_cast<Cache const&>(cache);
assert(result == T{3});
}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<class ...Args>
+// constexpr T& __emplace(Args&& ...);
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+
+template<int I>
+struct X {
+ int value = -1;
+ template<int J>
+ friend constexpr bool operator==(X const& a, X<J> const& b) { return I == J && a.value == b.value; }
+};
+
+struct NonMovable {
+ int value = -1;
+ NonMovable() = default;
+ constexpr explicit NonMovable(int v) : value(v) { }
+ NonMovable(NonMovable&&) = delete;
+ NonMovable& operator=(NonMovable&&) = delete;
+};
+
+constexpr bool test() {
+ {
+ using T = std::tuple<>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace();
+ assert(&result == &*cache);
+ assert(result == T());
+ }
+
+ {
+ using T = std::tuple<X<0>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace();
+ assert(&result == &*cache);
+ assert(result == T());
+ }
+ {
+ using T = std::tuple<X<0>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace(X<0>{0});
+ assert(&result == &*cache);
+ assert(result == T(X<0>{0}));
+ }
+
+ {
+ using T = std::tuple<X<0>, X<1>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace();
+ assert(&result == &*cache);
+ assert(result == T());
+ }
+ {
+ using T = std::tuple<X<0>, X<1>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace(X<0>{0}, X<1>{1});
+ assert(&result == &*cache);
+ assert(result == T(X<0>{0}, X<1>{1}));
+ }
+
+ // Make sure that we do not require the type to be movable when we emplace it.
+ // Move elision should be performed instead, see http://eel.is/c++draft/range.nonprop.cache#note-1.
+ {
+ using Cache = std::ranges::__non_propagating_cache<NonMovable>;
+ Cache cache;
+ NonMovable& result = cache.__emplace();
+ assert(&result == &*cache);
+ assert(result.value == -1);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ static_assert(test());
+ test();
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<class Fn>
+// constexpr T& __emplace_from(Fn const&);
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+
+template<int I>
+struct X {
+ int value = -1;
+ template<int J>
+ friend constexpr bool operator==(X const& a, X<J> const& b) { return I == J && a.value == b.value; }
+};
+
+struct NonMovable {
+ int value = -1;
+ NonMovable() = default;
+ constexpr explicit NonMovable(int v) : value(v) { }
+ NonMovable(NonMovable&&) = delete;
+ NonMovable& operator=(NonMovable&&) = delete;
+};
+
+constexpr bool test() {
+ {
+ using T = std::tuple<>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace_from([] { return T(); });
+ assert(&result == &*cache);
+ assert(result == T());
+ }
+ {
+ using T = std::tuple<X<0>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace_from([] { return T(X<0>{0}); });
+ assert(&result == &*cache);
+ assert(result == T(X<0>{0}));
+ }
+ {
+ using T = std::tuple<X<0>, X<1>>;
+ using Cache = std::ranges::__non_propagating_cache<T>;
+ Cache cache;
+ T& result = cache.__emplace_from([] { return T(X<0>{0}, X<1>{1}); });
+ assert(&result == &*cache);
+ assert(result == T(X<0>{0}, X<1>{1}));
+ }
+
+ // Make sure that we do not require the type to be movable when we emplace it.
+ // Move elision should be performed instead, see http://eel.is/c++draft/range.nonprop.cache#note-1.
+ {
+ using Cache = std::ranges::__non_propagating_cache<NonMovable>;
+ Cache cache;
+ NonMovable& result = cache.__emplace_from([] { return NonMovable(3); });
+ assert(&result == &*cache);
+ assert(result.value == 3);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ static_assert(test());
+ test();
+ return 0;
+}
// __has_value on a non-empty cache
{
- Cache cache; cache.__set(T{});
+ Cache cache; cache.__emplace();
assert(cache.__has_value());
}
}