Skip to content

Latest commit

 

History

History
149 lines (110 loc) · 5.16 KB

p0827r0.md

File metadata and controls

149 lines (110 loc) · 5.16 KB

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

General-Purpose Constant Value Type

Introduction

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:

  1. It requires that two template parameters be specified where template<auto> would mean only one was necessary.

  2. If future revisions to the standard relax restrictions on non-type template parameters, it will be ill-named and ill-prepared.

  3. 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.

Prior Art

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].

Details

Definition of constant

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>{});

Operator Overloads

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>{};
}

User-Defined Literals

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>

Discussion

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.

Reference Implementation

A simple proof of concept is available on [CompilerExplorer].