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 to value_type_traits<T, D>.
class default_difference

default_difference<T> provides a member typedef that indicates the default difference type for T.

In particular, if T is an unsigned integer type, default_difference<T>::type is std::make_signed<T>::type. In other cases, default_difference<T>::type is identical to T.

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 is value_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 sequence 0, 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 sequence 0, 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 to c.size() - 1. Here, the value type Siz is Container::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 always 1.
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 if pos >= 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.

constexpr iterator begin() const

Get a const iterator to the beginning, equivalent to cbegin().

constexpr iterator end() const

Get a const iterator to the end, equivalent to cend().

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.