The concepts-lite extension for c++ is accepted for the upcoming C++17 standard. Some tutorials are already available and with the current gcc trunk an experimental implementation of this feature in a c++ compiler. We will see some examples of concepts-lite in action.

The task is to write a library that overloads to operator* in order to scale an iterable object by a scalar factor, e.g. scale a vector by a double value. In order to be very generic, we want to write a templated function for the scaling:

template <class Vector, class Scalar>
Vector& operator*=(Vector& v, Scalar s)
{
  for (auto& v_i : v)
    v_i *= s;
  return v;
}

template <class Vector, class Scalar>
Vector const operator*(Vector v, Scalar s)
{
  return v *= s;
}

This implementation provides a scaling of a vector by a scalar factor from the right. The problem with this implementation is, that we can not distiguish between the vector argument and the scalar argument, since both are unconstrained templates. Passing a scalar as first argument and a vector as second argument is a valid call to the function, but results in a compiler error in the inner algorithm, i.e. in the range-based for-loop, since a scalar is not iterable.

It is possible to write an overload of operator* for a left-sided multiplication by using tag-dispatiching, SFINAE techniques, or some other tricks. But we want to write the code in a natural way. Therefore, we use constraints by the concepts-lite specification (Technical specification, Tutorial).

What is a vector? Or at least an iterable object? We require, that a range-based for-loop can be used for the container, i.e. there exist begin() and end() (member) functions and the current iterate, i.e. the value-type of a vector, is multiplicable by a scalar.

What is a scalar, on the other hand? For now we simply require, that it is an arithmetic type, that fulfills the type-trait std::is_arithmetic. Later, we can refine this concept, by explicitly specifying requirements on scalar arguments.

In order to use type-constraints, we have to define concepts:

template <typename V>
concept bool Iterable =
  requires(V v) {
    begin(v);
    end(v);
  } || requires(V v) {
    v.begin();
    v.end();
  };

template <typename S>
concept bool Arithmetic = std::is_arithmetic<S>::value;

template <typename T0, typename T1>
concept bool Multiplicable =
  requires(T0 t0, T1 t1) {
    t0 * t1;
    t1 * t0;
    t0 *= t1;
    t1 *= t0;
  };

The first one (Iterable) provides a simplified concept for iterable objects, i.e. there must exist either a free function begin() and end() or member functions with the same names. Here, nothing is said about what these functions should return. A more advanced implementation could be a requirement that the result of begin() and end() are Iterators (Input- or Output-Iterators, depending on other requirements). The second concepts simply redirects to the type-traits implementation of the STL. The third concepts is a binary concept, since it implements a relation between two types. Here, we just require, that a multiplication operation between these two types exist.

Combined with the operator definition above, we can now define vector*scalar and scalar*vector concept-enabled function overloads:

template <Iterable Vector, Arithmetic Scalar>
  requires Multiplicable<Value_type<Vector>, Scalar>
Vector& operator*=(Vector& v, Scalar s)
{
  for (auto& v_i : v)
    v_i *= s;
  return v;
}

template <Iterable Vector, Arithmetic Scalar>
Vector const operator*(Vector v, Scalar s)
{
  return v *= s;
}

template <Arithmetic Scalar, Iterable Vector>
Vector const operator*(Scalar s, Vector v)
{
  return v *= s;
}

where Value_type is an alias template leading to the value type of a container. A possible implementation could be:

namespace impl 
{
  template <typename V>
  struct Value_type;
  
  template <typename V>
    requires requires() { typename V::value_type; }
  struct Value_type<V> {
    using type = typename V::value_type;
  };
} // end namespace impl

template <typename V>
using Value_type = typename impl::Value_type<V>::type;

So, we can now multiply any iterable object by any arithmetic type, if the value-types are multiplicable, e.g.

std::vector<double>   v1{1.0,2.0,3.0};
std::array<double, 3> v2{1.0,2.0,3.0};
std::list<float>      v3{1.0,2.0,3.0};

auto v1_scaled = v1 * 3;
auto v2_scaled = v2 * 3.0;
auto v3_scaled = v3 * 3.0;

auto v4_scaled = 3 * v1;

Download Source

You can download the source of the final implementation from here.