Allowing any unsigned integral type as parameter type for literal operators

Author: MichaƂ Dominiak, griwes@griwes.info
Document number: P0345R0
Date: 2016-05-25

Introduction

User-defined literals are a great tool for shortening code that would otherwise need to be overly verbose, but since the first proposal, they lacked one feature that would make writing code even simpler - supporting arbitrary unsigned types as arguments for operator"". Consider the following code:

    constexpr std::uint8_t operator "" _foo(unsigned long long value)
    {
        return value;
    }

    int main()
    {
        auto foo = 1024_foo;    // this compiles with no warnings, even though it overflows, whereas this:
        char bar = 1024;        // warns about changing the value
    }
            

It should be obvious that the first should also produce a diagnostic, since it might be a result of a simple programming mistake. There is also another problem, which appears to be far less important, but still exists. Consider the following platform specific code for a platform, where std::uint64_t is the biggest unsigned integer type:

    constexpr std::uint64_t operator "" _bar(std::uint64_t value)
    {
        // ...
    }
            

results in an error in both GCC and Clang, because they use unsigned long, not unsigned long long for std::uint64_t, at least on Linux. This is an environment-specific problem, but the Standard should provide a way to overcome it in a portable way.

Proposed solution

The proposed solution is as follows:

  1. Allow any unsigned type as an argument for operator "", not unsigned long long only.
  2. Make narrowing in case of user defined literals ill-formed.

An additional feature gained with 1) and 2) is allowing to limit the range of values used in a user-defined literal.

Note: this proposal doesn't propose operator "" overloading for unsigned integral types. Allowing such overloading would create ambiguities; it is not desirable.

Proposed wording changes

Change in 13.5.8 over.literal paragraph 3:

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:
Add two new paragraphs before 13.5.8 over.literal paragraph 5:
Literal operator overloading for unsigned integral types is not supported. If there exist two or more literal operator declarations with the same literal suffix identifier with a single parameter, but of different unsigned integral types, the program is ill-formed.

If a narrowing conversion would happen when a literal operator with a single parameter of an unsigned integral type is invoked implicitly through user-defined literal, the program is ill-formed.
Change in 13.5.8 over.literal paragraph 8:
    template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause
    std::uint8_t operator "" _k(std::uint8_t);
    auto l = 256_k;                                     // error: integer constant is too large for its type
    std::uint16_t operator "" _k(std::uint16_t);        // error: literal operator overloading is not supported