Allowing signed char and unsigned char in random number generation
- Document number:
- D4037R0
- Date:
2026-02-26 - Audience:
- LEWG
- Project:
- ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
- Reply-to:
- Jan Schultke <janschultke@gmail.com>
- GitHub Issue:
- wg21.link/P4037/github
- Source:
- github.com/eisenwave/cpp-proposals/blob/master/src/random-chars.cow
or in
currently results in undefined behavior.
Instead, it should be permitted.
Contents
Introduction
Motivation
Design
Which character types to support
Whether to diagnose unsupported types
Implementation experience
Wording
References
1. Introduction
While it may seem perfectly reasonable to generate random bytes
in the form of ,
[rand.req.genl] requires that where
appears as a template parameter name (such as in ),
providing as an argument causes template instantiation
to have undefined behavior.
This is functionally equivalent to making the program ill-formed, no diagnostic required.
In 2013,
[LWG2326]
stated that
should be permittedit's just silly that we have a random number library with no natural way to generate random bytes
.
As silly as it may be,
the issue was closed as NAD (not a defect)
because it was perceived as a feature request rather than the resolution of a defect.
Some discussion of a writing a paper took place,
but that paper never manifested.
It is unclear why the restriction exists in the first place;
I was unable to find any discussion in published papers.
The restriction dates back at least to [N1932], published 2006.
One may suspect that it has to do with
and other single-byte types being subject to integer promotion (to ),
but paradoxically,
and are supported
despite undergoing promotion.
In any case, and should be supported.
2. Motivation
The ability to generate random bytes or octets is extremely valuable for fuzz testing. For example, when testing an implementation of an LZ77 or LZ78 compression algorithm, a user would typically
- generate a random sequence of bytes,
- compress that sequence using their algorithm,
- decompress the result, and
- compare whether the decompressed bytes are the same as the original bytes.
It is easy to imagine testing methodology that involves random bytes for any kind of codec, network protocol, compiler, syntax highlighter, etc.
3. Design
3.1. Which character types to support
The current restriction which forbids is bad,
but it's not obvious how much the restriction should be relaxed.
There are a few plausible options:
-
Permit all standard integers.
That is, the current restriction, plus
andsigned char .unsigned char -
Permit all standard integers and
.char - Permit all integral types.
Permitting all integral types imposes an unreasonable implementation burden.
One of the biggest issues is
assuming the implementation provides an extended 128-bit integer type,
which recent version of GCC and Clang do.
libstdc++ permits in most of ,
but rejects
using:
See https://github.com/gcc-mirror/gcc/blob/0635bfb53a145cc005a15657635abf8ea9e6e9ba/libstdc%2B%2B-v3/include/bits/random.h#L521-L522.
The difficulty lies in the fact that
uses modular arithmetic,
which requires N×2-bit division for N-bit integer types.
For ,
the existence of makes this easy,
but for , 256-bit division would be required.
Permitting only in addition to standard integer types seems unmotivated,
although there is precedent for this in .
If is supported, why not character types in general?
If characters types in general are supported, why not integral types and unscoped enumerations?
The design becomes somewhat arbitrary.
Overall, permitting only standard integers is an obvious and minimal fix.
or as ,
even if has the correct signedness.
3.2. Whether to diagnose unsupported types
It may also be desirable to diagnose the use of unsupported types in ,
rather than treating it as IFNDR.
However, this would raise warnings in substantial amounts of existing code
without providing much benefit to users.
For instance, GCC has supported all integral types in for years now,
and numerous uses of
and other disallowed types can be found.
Therefore, it may be best to simply perpetuate the status quo and leave diagnostics up to implementations. This effectively means that [rand.req.gen] provides a minimal list of supported types, which the implementation optionally extends.
4. Implementation experience
At the time of writing,
the MSVC STL has a which prevents instantiation
of ; see:
https://github.com/microsoft/STL/blob/9b021cf66875d2fbf4627aad8db594595aebb148/stl/inc/random#L43
libc++ supports and as an extension; see:
https://github.com/llvm/llvm-project/blob/9d075d271ff15ec153ada3413d294540206a15ed/libcxx/include/__random/is_valid.h#L49.
libstdc++ supports any integral type as an extension; see:
https://github.com/gcc-mirror/gcc/blob/0635bfb53a145cc005a15657635abf8ea9e6e9ba/libstdc%2B%2B-v3/include/bits/uniform_int_dist.h#L90-L91.
5. Wording
The changes are relative to [N5032].
Add a feature-test macro to [version.syn] as follows:
Change [rand.req.general] paragraph 1 as follows:
Throughout [rand], the effect of instantiating a template:
-
that has a template type parameter named
is undefined unless the corresponding template argument is cv-unqualified and meets the requirements of seed sequence ([rand.req.seedseq]).Sseq -
that has a template type parameter named
is undefined unless the corresponding template argument is cv-unqualified and meets the requirements of uniform random bit generator ([rand.req.urng]).URBG -
that has a template type parameter named
is undefined unless the corresponding template argument is cv-unqualified and meets the requirements of random number engine ([rand.req.eng]).Engine -
that has a template type parameter named
is undefined unless the corresponding template argumentRealType is cv-unqualified andis one of,float , ordouble .long double -
that has a template type parameter named
is undefined unless the corresponding template argument isIntType cv-unqualified and is one ofa standard integer type ([basic.fundamental]).,short ,int ,long ,long long ,unsigned short ,unsigned int , orunsigned long unsigned long long -
that has a template type parameter named
is undefined unless the corresponding template argument isUIntType cv-unqualified and is one ofa standard unsigned integer type ([basic.fundamental]).,unsigned short ,unsigned int , orunsigned long unsigned long long