Document number: P0827R0
Date: 2017-10-16
Reply-to: John McFarlane, cnl@john.mcfarlane.name
Reply-to: Louis Dionne, ldionne.2@gmail.com
Audience: SG6, SG14, LEWG
A constant value type can make some arithmetic operations more efficient. For example, some low-level operations using the fixed-point type in [P0037] effectively become no-ops:
auto a = fixed_point<int, -10>{1}; // fixed-point wrapper around an int with resolution 2^-10
auto b = a << 1; // fixed_point<int, -10>{2}, requires a run-time shift operation
auto c = a << integral_constant<int, 1>{}; // fixed_point<int, -9>{2}, bitwise equal to a; only the type has changed
Combined with a user-defined literal, this code can be made almost as terse and readable as the run-time code:
auto d = a << 1static;
Many other examples exists of situations where the value of an argument might affect the type of a result:
// snug returns the narrowest type that can hold the given value
auto e = snug(100); // sizeof(e) == sizeof(int), snug cannot determine type from value
auto f = snug(100static); // sizeof(f) == sizeof(int8_t), but it can determine type from type
This feature would also interact well with class template argument deduction:
auto g = fixed_point(0x100); // fixed_point<int, 0>{256}, type cannot be determined based on initial value
auto h = fixed_point(0x100static); // fixed_point<int, 8>{256}, 8 fewer bits are devoted to low-order bits
Currently, the only standard type for expressing a constant value types is integral_constant
which has drawbacks:
-
It requires that two template parameters be specified where
template<auto>
would mean only one was necessary. -
If future revisions to the standard relax restrictions on non-type template parameters, it will be ill-named and ill-prepared.
-
A lack of operator overloads means that results of many operations are values, e.g.:
auto g = integral_constant<int, 2>{} + integral_constant<int, 2>{}; // result is 4, not integral_constant<int, 4>{}
We propose a replacement for integral_constant
called constant
and an accompanying user-defined literal
which address the above issues.
A similar proposal to improve on integral_constant
was made
in P0377.
It addresses the first of the three drawbacks listed above:
namely eliminating integral_constant
's type template parameter.
But it does not address the other two.
User-defined literals returning an constant value type can be found in multiple libraries and on forums including in [Boost.Hana] and [CNL].
template<auto Value>
struct constant {
using value_type = decltype(Value);
static constexpr value_type value = Value;
constexpr operator value_type() const {
return value;
}
};
The implicit conversion operator ensures that constant
objects are as easy to use as the variable they mimic.
In the following example, the result of !=
is constant<false>
which is then implicitly converted to bool
:
static_assert(constant<1>{} != constant<2>{});
A complete set of unary and binary operators ensure
that operations taking only constant
operands do not return non-constant
results:
// unary operator @
template<auto Value1>
constexpr auto operator@(constant<Value1> rhs) noexcept {
return constant<@ Value1>{};
}
// binary operator @
template<auto Value1, auto Value2>
constexpr auto operator@(constant<Value1> lhs, constant<Value2> rhs) noexcept {
return constant<Value1 @ Value2>{};
}
A user-defined literal returns constant
objects of values of type, maxint_t
.
namespace literals {
template<char ... Chars>
constexpr auto operator ""static() noexcept;
}
(Suffix, static
, is by no means final.
Unfortunately, preferred suffix, c
, is not suitable for combination with hexadecimal literals.)
The input string could contain whole numbers in a variety of bases:
using namespace literals;
auto i = 1000000000000static; // constant<1000000000000LL>
auto j = 0x401static; // constant<1025LL>
auto k = 0b10000static; // constant<16LL>
auto l = 077static; // constant<63LL>
The use of a pack of char
as input to operator ""static
is limiting.
However, there is currently no better alternative.
Future language revisions may allow some improvements:
- fractional values, e.g. with a decimal place;
- non-string input and
- greater range than
intmax_t
.
Care should be taken to ensure the current design does not preclude these possibilities.
A simple proof of concept is available on [CompilerExplorer].