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 constexpr
ness of the object itself.
All Constant
s must be somewhat equivalent, in the following sense. Let C(T)
and D(U)
denote the tags of Constant
s 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 Constant
s 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.
value
and to
, satisfying the laws below.
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:
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 Constant
s 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:
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
:
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.
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.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
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:
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 constexpr
ness 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
which is an embedding, and
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
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.
Because of the requirement that Constant
s be interchangeable when their contents are compatible, two Constant
s 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 Constant
s 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... | |
constexpr auto boost::hana::value |
#include <boost/hana/fwd/value.hpp>
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.
However, for convenience, an overload of value
is provided so that it can be called as:
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.
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.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>
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.
value
, and hence it is not tag-dispatched and can't be customized.