Value Range¶
It is a very common pattern in C/C++ programming to write loops that enumerate values within a certain range, such as
for (int i = 0; i < n; ++i) {
// do something
}
In C++11, the range for-loop syntax is introduced, which allow concise
expression of the looping over elements in a container. However, one has to
resort to the old pattern when enumerating values. Here, we provide a class
template value_range
that wraps a range of values to a light-weight
container-like object. Below is an example:
#include <clue/value_range.hpp>
using namespace clue;
size_t n = 10
// enumerate i from 0 to n-1
for (size_t i: vrange(n)) {
// do something on i
}
double a = 2.0, b = 9.0;
// enumerate v from 2.0 to 8.0
for (auto v: vrange(a, b)) {
// do something on i
}
std::vector<int> a{1, 2, 3, 4, 5};
std::vector<int> b{5, 6, 7, 8, 9};
std::vector<int> r
// enumrate i from 0 to a.size() - 1
for (auto i: indices(a)) {
r.push_back(a[i] + b[i]);
}
Documentation of value_range
and relevant functions are given below.
The value_range
and stepped_value_range
class templates¶
Formally, the class template value_range
is defined as:
-
class
value_range
¶ Formal: template<typename T, typename D=typename default_difference<T>::type, typename Traits=value_range_traits<T, D>> class value_range;
Classes to represent continuous value ranges, such as
1, 2, 3, 4, ...
.Parameters: - T – The value type.
- D – The difference type. This can be omitted, and it will be,
by default, set to
default_difference<T>::type
. - Traits – A traits class that specifies the behavior of the
value type
T
. This class has to satisfy the EnumerableValueTraits concept, which will be explained in the section enumerable_value_traits. In general, one may omit this, and it will be, by default, set tovalue_type_traits<T, D>
.
-
class
default_difference
¶ default_difference<T>
provides a member typedef that indicates the default difference type forT
.In particular, if
T
is an unsigned integer type,default_difference<T>::type
isstd::make_signed<T>::type
. In other cases,default_difference<T>::type
is identical toT
.To enumerate non-numerical types (e.g. dates), one should specialize
default_difference<T>
to provide a suitable difference type.
-
class
stepped_value_range
¶ Formal: template<typename T, typename S, typename D=typename default_difference<T>::type, typename Traits=value_range_traits<T, D>> class stepped_value_range;
Classes to represent stepped ranges, such as
1, 3, 5, 7, ...
.Parameters: - T – The value type.
- S – The step type.
- D – The difference type. By default, it is
default_difference_type<T>::type
. - Traits – The trait class for
T
. By default, it isvalue_type_traits<T, D>
.
Note
For stepped_value_range<T, S>
, only unsigned integral types for T
and S
are supported at this point.
Member types¶
The class value_range<T>
or stepped_value_range<T, S>
contains a series
of member typedefs as follows:
types | definitions |
value_type |
T |
difference_type |
D |
step_type |
S |
traits_type |
Traits |
size_type |
std::size_t |
pointer |
const T* |
const_pointer |
const T* |
reference |
const T& |
const_reference |
const T& |
iterator |
implementing RandomAccessIterator |
const_iterator |
iterator |
Note
For value_range<T>
, the step_type
is the same as size_type
.
Construction¶
The value_range<T>
and stepped_value_range<T, S>
classes have simple
constructors.
-
constexpr
value_range
(const T &vbegin, const T &vend)¶ Parameters: - vbegin – The beginning value (inclusive).
- vend – The ending value (exclusive).
For example,
value_range(0, 3)
indicates the following sequence0, 1, 2
.
-
stepped_value_range
(const T &vbegin, const T &vend, const S &step)¶ Parameters: - vbegin – The beginning value (inclusive).
- vend – The ending value (exclusive).
- step – The incremental step.
For example,
stepped_value_range(0, 2, 5)
indicates the following sequence0, 2, 4
.
Note
These classes also have a copy constructor, an assignment operator, a
destructor and a swap
member function, all with default behaviors.
Note
For stepped ranges, the step must be positive. Zero or negative step
would result in undefined behavior. The size of a stepped range is computed
as (e - b + (s - 1)) / s
.
In addition, convenient constructing functions are provided, with which the user does not need to explictly specify the value type (which would be infered from the arguments):
-
constexpr value_range<T>
vrange
(const T &u)¶ Equivalent to
value_range<T>(static_cast<T>(0), u)
.
-
constexpr value_range<T>
vrange
(const T &a, const T &b)¶ Equivalent to
value_range<T>(a, b)
.
-
value_range<Siz>
indices
(const Container &c)¶ Returns a value range that contains indices from
0
toc.size() - 1
. Here, the value typeSiz
isContainer::size_type
.
Properties and element access¶
The value_range<T>
and stepped_value_range<T, S>
classes provide a
similar set of member functions that allow access of the basic properties and
individual values in the range, as follows.
-
constexpr size_type
size
() const noexcept¶ Get the size of the range, i.e. the number of values contained in the range.
-
constexpr bool
empty
() const noexcept¶ Get whether the range is empty, i.e. contains no values.
-
constexpr size_type
step
() const noexcept¶ Get the step size.
Note: For value_range<T>
, the step size is always1
.
-
constexpr T
front
() const noexcept¶ Get the first value within the range.
-
constexpr T
back
() const noexcept¶ Get the last value within the range.
-
constexpr T
begin_value
() const noexcept¶ Get the first value in the range (equivalent to
front()
).
-
constexpr T
end_value
() const noexcept¶ Get the value that specifies the end of the value, which is the value next to
back()
.
-
constexpr T
operator[]
(size_type pos) const¶ Get the value at position
pos
, withou bounds checking.
-
constexpr T
at
(size_type pos) const¶ Get the value at position
pos
, with bounds checking.Throw: an exception of class std::out_of_range
ifpos >= size()
.
Iterators¶
-
constexpr const_iterator
cbegin
() const¶ Get a const iterator to the beginning.
-
constexpr const_iterator
cend
() const¶ Get a const iterator to the end.
Note
A value range or stepped value range does not actually store the values in
the range. Hence, the iterators are proxies that do not refer to an
existing location in memory. Instead, *iter
returns the value itself
instead of a reference. In spite of this subtle difference from a typical
iterator, we find that it works perfectly with most STL algorithms.
The EnumerableValueTraits concept¶
The class template value_range
has a type parameter Traits
, which has to
satisfy the following concept.
// x, y are values of type T, and n is a value of type D
Traits::increment(x); // in-place increment of x
Traits::decrement(x); // in-place decrement of x
Traits::increment(x, n); // in-place increment of x by n units
Traits::decrement(x, n); // in-place decrement of x by n units
Traits::next(x); // return the value next to x
Traits::prev(x); // return the value that precedes x
Traits::next(x, n); // return the value ahead of x by n units
Traits::prev(x, n); // return the value behind x by n units
Traits::eq(x, y); // whether x is equal to y
Traits::lt(x, y); // whether x is less than y
Traits::le(x, y); // whether x is less than or equal to y
Traits::difference(x, y); // the difference between x and y, i.e. x - y
By default, the builtin value_range_traits<T, D>
would be used and users
don’t have to specify the traits explicitly. However, one can specify a
different trait class to provide special behaviors.