interval: change start()/end() not to return references to data members

We'd like to change the data layout of `interval` to save space.
As a result, start() and end() which return references to data
members must return objects (not references). Since we'd like
to maintain zero-copy for these functions, we change them to
return objects containing references (rather than references
to objects), avoiding copying of potentially expensive objects.

We repurpose the interval_bound class to hold references (by
instantiating it with `const T&` instead of `T`) and provide
converting constructors. To make transform_bounds() retain
zero-copy, we add start() and end() that take *this by
rvalue reference.
This commit is contained in:
Avi Kivity
2025-05-20 11:13:00 +03:00
parent 16fb68bb5e
commit f3dccc2215
2 changed files with 38 additions and 10 deletions

View File

@@ -14,14 +14,14 @@ class interval_bound {
template<typename T>
class wrapping_interval {
std::optional<interval_bound<T>> start();
std::optional<interval_bound<T>> end();
std::optional<interval_bound<T>> start_copy();
std::optional<interval_bound<T>> end_copy();
bool is_singular();
};
template<typename T>
class interval {
std::optional<interval_bound<T>> start();
std::optional<interval_bound<T>> end();
std::optional<interval_bound<T>> start_copy();
std::optional<interval_bound<T>> end_copy();
bool is_singular();
};

View File

@@ -52,6 +52,12 @@ public:
: _value(std::move(value))
, _inclusive(inclusive)
{ }
interval_bound(interval_bound<const T&> b) requires (!std::is_reference_v<T>)
: interval_bound(b.value(), b.is_inclusive())
{ }
operator interval_bound<const T&>() const requires (!std::is_reference_v<T>) {
return interval_bound<const T&>(_value, _inclusive);
}
const T& value() const & { return _value; }
T&& value() && { return std::move(_value); }
bool is_inclusive() const { return _inclusive; }
@@ -63,6 +69,9 @@ public:
}
};
template <typename T>
using interval_bound_const_ref = interval_bound<const T&>;
template<typename T>
class interval;
@@ -74,6 +83,7 @@ class wrapping_interval {
using optional = std::optional<U>;
public:
using bound = interval_bound<T>;
using bound_const_ref = interval_bound_const_ref<T>;
template <typename Transformer>
using transformed_type = typename std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<Transformer, T>>>;
@@ -97,8 +107,8 @@ public:
constexpr wrapping_interval() : wrapping_interval({}, {}) { }
private:
// Bound wrappers for compile-time dispatch and safety.
struct start_bound_ref { const optional<bound>& b; };
struct end_bound_ref { const optional<bound>& b; };
struct start_bound_ref { const optional<bound_const_ref> b; };
struct end_bound_ref { const optional<bound_const_ref> b; };
start_bound_ref start_bound() const { return { start() }; }
end_bound_ref end_bound() const { return { end() }; }
@@ -248,15 +258,23 @@ public:
std::swap(_start, _end);
}
}
const optional<bound>& start() const {
/// Returns the start bound of the interval, as a view into the interval object.
/// Copying the bound still points into the original interval object.
optional<bound_const_ref> start() const {
return _start;
}
const optional<bound>& end() const {
/// Returns the end bound of the interval, as a view into the interval object.
/// Copying the bound still points into the original interval object.
optional<bound_const_ref> end() const {
return _singular ? _start : _end;
}
/// Returns the start bound of the interval, as a materialized object
/// independent of the original interval.
optional<bound> start_copy() const {
return _start;
}
/// Returns the end bound of the interval, as a materialized object
/// independent of the original interval.
optional<bound> end_copy() const {
return _singular ? _start : _end;
}
@@ -484,6 +502,7 @@ class interval {
using optional = std::optional<U>;
public:
using bound = interval_bound<T>;
using bound_const_ref = interval_bound_const_ref<T>;
template <typename Transformer>
using transformed_type = typename wrapping_interval<T>::template transformed_type<Transformer>;
@@ -560,15 +579,24 @@ public:
bool is_full() const {
return _interval.is_full();
}
const optional<bound>& start() const {
/// Returns the start bound of the interval, as a view into the interval object.
/// Copying the bound still points into the original interval object.
const optional<bound_const_ref> start() const {
return _interval.start();
}
const optional<bound>& end() const {
/// Returns the end bound of the interval, as a view into the interval object.
/// Copying the bound still points into the original interval object.
const optional<bound_const_ref> end() const {
return _interval.end();
}
/// Returns the start bound of the interval, as a materialized object
/// independent of the original interval.
optional<bound> start_copy() const {
return _interval.start_copy();
}
/// Returns the end bound of the interval, as a materialized object
/// independent of the original interval.
optional<bound> end_copy() const {
return _interval.end_copy();
}