From 3466a6f8184d64777072f28da92730f11c9e0e47 Mon Sep 17 00:00:00 2001 From: Kp Date: Mon, 28 Jun 2021 03:37:50 +0000 Subject: [PATCH] Combine partial_range template constructors Add an explicit check for rvalue ranges that own their storage, and then allow any other rvalue input. --- common/include/partial_range.h | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/common/include/partial_range.h b/common/include/partial_range.h index 3a84f4511..85aa2c4b4 100644 --- a/common/include/partial_range.h +++ b/common/include/partial_range.h @@ -119,14 +119,29 @@ public: partial_range_t(partial_range_t &&) = default; partial_range_t &operator=(const partial_range_t &) = default; template - partial_range_t(T &t) : + partial_range_t(T &&t) : m_begin(partial_range_detail::adl_begin(t)), m_end(partial_range_detail::adl_end(t)) { - } - template - partial_range_t(partial_range_t &&t) : - m_begin(t.begin()), m_end(t.end()) - { + /* If `T &&`, after reference collapsing, is an lvalue + * reference, then the object referenced by `t` will remain in + * scope after the statement that called this constructor, and + * there is no need to check whether the object `t` owns the + * iterated range. + * + * Otherwise, if `t` is an rvalue reference, assert that `t` is + * a view onto a range, rather than owning the range. A `t` + * that owns the storage would leave the iterators dangling. + * + * These checks are not precise. It is possible to have a type + * T that remains in scope, but frees its storage early, and + * leaves the range dangling. It is possible to have a type T + * that owns the storage, and is not destroyed after the + * containing statement terminates. Neither are good designs, + * and neither can be handled here. These checks attempt to + * catch obvious mistakes. + */ + if constexpr (!std::is_lvalue_reference::value) + static_assert(!T::range_owns_iterated_storage::value, "rvalue reference to range requires that the range is a view, not an owner"); } __attribute_warn_unused_result iterator begin() const { return m_begin; }