Boost.Hana  1.5.0
Your standard library for metaprogramming
Constant

Description

The Constant concept represents data that can be manipulated at compile-time.

At its core, Constant is simply a generalization of the principle behind std::integral_constant to all types that can be constructed at compile-time, i.e. to all types with a constexpr constructor (also called Literal types). More specifically, a Constant is an object from which a constexpr value may be obtained (through the value method) regardless of the constexprness of the object itself.

All Constants must be somewhat equivalent, in the following sense. Let C(T) and D(U) denote the tags of Constants holding objects of type T and U, respectively. Then, an object with tag D(U) must be convertible to an object with tag C(T) whenever U is convertible to T, as determined by is_convertible. The interpretation here is that a Constant is just a box holding an object of some type, and it should be possible to swap between boxes whenever the objects inside the boxes can be swapped.

Because of this last requirement, one could be tempted to think that specialized "boxes" like std::integral_constant are prevented from being Constants because they are not able to hold objects of any type T (std::integral_constant may only hold integral types). This is false; the requirement should be interpreted as saying that whenever C(T) is meaningful (e.g. only when T is integral for std::integral_constant) and there exists a conversion from U to T, then a conversion from D(U) to C(T) should also exist. The precise requirements for being a Constant are embodied in the following laws.

Minimal complete definition

value and to, satisfying the laws below.

Laws

Let c be an object of with tag C, which represents a Constant holding an object with tag T. The first law ensures that the value of the wrapped object is always a constant expression by requiring the following to be well-formed:

constexpr auto x = hana::value<decltype(c)>();

This means that the value function must return an object that can be constructed at compile-time. It is important to note how value only receives the type of the object and not the object itself. This is the core of the Constant concept; it means that the only information required to implement value must be stored in the type of its argument, and hence be available statically.

The second law that must be satisfied ensures that Constants are basically dumb boxes, which makes it possible to provide models for many concepts without much work from the user. The law simply asks for the following expression to be valid:

to<C>(i)

where, i is an arbitrary Constant holding an internal value with a tag that can be converted to T, as determined by the hana::is_convertible metafunction. In other words, whenever U is convertible to T, a Constant holding a U is convertible to a Constant holding a T, if such a Constant can be created.

Finally, the tag C must provide a nested value_type alias to T, which allows us to query the tag of the inner value held by objects with tag C. In other words, the following must be true for any object c with tag C:

std::is_same<
C::value_type,
tag_of<decltype(hana::value(c))>::type

Refined concepts

In certain cases, a Constant can automatically be made a model of another concept. In particular, if a Constant C is holding an object of tag T, and if T models a concept X, then C may in most cases model X by simply performing whatever operation is required on its underlying value, and then wrapping the result back in a C.

More specifically, if a Constant C has an underlying value (C::value_type) which is a model of Comparable, Orderable, Logical, or Monoid up to EuclideanRing, then C must also be a model of those concepts. In other words, when C::value_type models one of the listed concepts, C itself must also model that concept. However, note that free models are provided for all of those concepts, so no additional work must be done.

While it would be possible in theory to provide models for concepts like Foldable too, only a couple of concepts are useful to have as Constant in practice. Providing free models for the concepts listed above is useful because it allows various types of integral constants (std::integral_constant, mpl::integral_c, etc...) to easily have models for them just by defining the Constant concept.

Remarks
An interesting observation is that Constant is actually the canonical embedding of the subcategory of constexpr things into the Hana category, which contains everything in this library. Hence, whatever is true in that subcategory is also true here, via this functor. This is why we can provide models of any concept that works on constexpr things for Constants, by simply passing them through that embedding.

Concrete models

hana::integral_constant

Provided conversion to the tag of the underlying value

Any Constant c holding an underlying value of tag T is convertible to any tag U such that T is convertible to U. Specifically, the conversion is equivalent to

to<U>(c) == to<U>(value<decltype(c)>())

Also, those conversions are marked as an embedding whenever the conversion of underlying types is an embedding. This is to allow Constants to inter-operate with constexpr objects easily:

plus(int_c<1>, 1) == 2

Strictly speaking, this is sometimes a violation of what it means to be an embedding. Indeed, while there exists an embedding from any Constant to a constexpr object (since Constant is just the canonical inclusion), there is no embedding from a Constant to a runtime object since we would lose the ability to define the value method (the constexprness of the object would have been lost). Since there is no way to distinguish constexpr and non-constexpr objects based on their type, Hana has no way to know whether the conversion is to a constexpr object of not. In other words, the to method has no way to differentiate between

constexpr int i = hana::to<int>(int_c<1>);

which is an embedding, and

int i = hana::to<int>(int_c<1>);

which isn't. To be on the safer side, we could mark the conversion as not-an-embedding. However, if e.g. the conversion from integral_constant_tag<int> to int was not marked as an embedding, we would have to write plus(to<int>(int_c<1>), 1) instead of just plus(int_c<1>, 1), which is cumbersome. Hence, the conversion is marked as an embedding, but this also means that code like

int i = 1;
plus(int_c<1>, i);

will be considered valid, which implicitly loses the fact that int_c<1> is a Constant, and hence does not follow the usual rules for cross-type operations in Hana.

Provided common data type

Because of the requirement that Constants be interchangeable when their contents are compatible, two Constants A and B will have a common data type whenever A::value_type and B::value_type have one. Their common data type is an unspecified Constant C such that C::value_type is exactly common_t<A::value_type, B::value_type>. A specialization of the common metafunction is provided for Constants to reflect this.

In the same vein, a common data type is also provided from any constant A to a type T such that A::value_type and T share a common type. The common type between A and T is obviously the common type between A::value_type and T. As explained above in the section on conversions, this is sometimes a violation of the definition of a common type, because there must be an embedding to the common type, which is not always the case. For the same reasons as explained above, this common type is still provided.

Variables

template<typename T >
constexpr auto boost::hana::value
 Return the compile-time value associated to a constant.This function returns the value associated to a Constant. That value is always a constant expression. The normal way of using value on an object c is. More...
 
constexpr auto boost::hana::value_of
 Equivalent to value, but can be passed to higher-order algorithms.This function object is equivalent to value, except it can be passed to higher order algorithms because it is a function object. value can't be passed to higher-order algorithms because it is implemented as an overloaded function. More...
 

Variable Documentation

template<typename T >
constexpr auto boost::hana::value

#include <boost/hana/fwd/value.hpp>

Initial value:
= []() -> decltype(auto) {
return tag-dispatched;
}

Return the compile-time value associated to a constant.This function returns the value associated to a Constant. That value is always a constant expression. The normal way of using value on an object c is.

constexpr auto result = hana::value<decltype(c)>();

However, for convenience, an overload of value is provided so that it can be called as:

constexpr auto result = hana::value(c);

This overload works by taking a const& to its argument, and then forwarding to the first version of value. Since it does not use its argument, the result can still be a constant expression, even if the argument is not a constant expression.

Note
value<T>() is tag-dispatched as value_impl<C>::apply<T>(), where C is the tag of T.
hana::value is an overloaded function, not a function object. Hence, it can't be passed to higher-order algorithms. If you need an equivalent function object, use hana::value_of instead.

Example

// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
int main() {
auto i = hana::integral_c<int, 3>; // notice no constexpr
static_assert(hana::value<decltype(i)>() == 3, "");
static_assert(hana::value(i) == 3, "value(i) is always a constant expression!");
}

Referenced by boost::hana::literals::operator""_c(), boost::hana::literals::operator""_s(), and boost::hana::optional< T >::optional().

constexpr auto boost::hana::value_of

#include <boost/hana/fwd/value.hpp>

Initial value:
= [](auto const& c) -> decltype(auto) {
return hana::value(c);
}

Equivalent to value, but can be passed to higher-order algorithms.This function object is equivalent to value, except it can be passed to higher order algorithms because it is a function object. value can't be passed to higher-order algorithms because it is implemented as an overloaded function.

Note
This function is a simple alias to value, and hence it is not tag-dispatched and can't be customized.

Example

// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
int main() {
constexpr auto xs = hana::tuple_c<int, 1, 2, 3, 4, 5>;
constexpr auto vs = hana::transform(xs, hana::value_of);
static_assert(vs == hana::make_tuple(1, 2, 3, 4, 5), "");
}