Date: Fri, 18 Sep 2026 17:21:19 +0000
Hm, interesting, yeah thanks for mentioning that. I suppose as the paper elsewhere points out, a compiler would still be free to provide their own _ExtInt types that do the same thing as _BitInt but which are just their own separate, extended integer types. In which case I would presume it would have the same behavior as I lined out before? Just with _ExtInt instead of _BitInt.
On Friday, September 18th, 2026 at 12:06 PM, Jan Schultke <janschultke_at_[hidden]> wrote:
> It is worth mentioning that if P3666 is accepted, then _BitInt will not be an eligible choice for make_signed<char16_t> or make_unsigned<char16_t>, only standard or extended integer types.
>
> See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3666r4.html#meta.trans.sign
>
> On Fri, 18 Sept 2026 at 16:08, Keenan Horrigan via Std-Discussion <std-discussion_at_[hidden]> wrote:
>
>> Perhaps there's a more grounded example available using the _BitInt types, which Clang provides in C++ as extended integer types.
>>
>> The following code successfully compiles today on Clang:
>>
>> __extension__ using uint9_ext_t = unsigned _BitInt(9);
>>
>> static_assert(std::integral<uint9_ext_t>);
>> static_assert(sizeof(uint9_ext_t) == 2);
>> static_assert(std::numeric_limits<uint9_ext_t>::max() == 511);
>>
>> static_assert(std::integral<char16_t>);
>> static_assert(sizeof(char16_t) == 2);
>> static_assert(std::numeric_limits<char16_t>::max() == 65535);
>>
>> static_assert(std::same_as<std::make_unsigned_t<char16_t>, unsigned short>);
>>
>> Godbolt link: https://godbolt.org/z/8a5666rWd
>>
>> According to the 'make_unsigned' rules, 'make_unsigned<char16_t>' should be providing 'uint9_ext_t' here before it would provide the standard integer type 'unsigned short', right? But of course that would be undesirable because 'uint9_ext_t' is more narrow than 'char16_t', despite having the same size and the same sign.
>>
>> On Thursday, September 17th, 2026 at 1:56 AM, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>>
>>> Hello,
>>>
>>> Recently I've been working on some code that needs to deal somewhat finely with integer/integral types, so I've been going and reading the standard to make sure that I'm staying within the bounds of the standard with regards to integer types. Something that caught my eye, and is somewhat meaningful to my code, is the specification of 'std::make_unsigned', which can be seen here: https://eel.is/c++draft/meta.trans.sign
>>>
>>> For signed and unsigned integer types, it works how I would expect, but for other types it's specified as yielding "the unsigned integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type)". And what's confusing to me about that is that it talks about the *size* of the type, rather than the *width* of the type, which I think could hypothetically lead to getting a type which may have the same size as the original type, but which has a smaller width and therefore a smaller value range.
>>>
>>> For instance, we might try to calculate 'make_unsigned_t<char16_t>', and let's assume that we're on a platform where char16_t has a size of two bytes and a width of 16. To my eye, that call to 'make_unsigned' is not required to yield what one might expect, that being the actual underlying type of char16_t, 'std::uint_least16_t', nor even a type that has the same properties. If we were to imagine that our implementation provides an extended unsigned integer type that has a size of two bytes, but which has 8 padding bits and a width of 8, then, if I'm understanding all these rules correctly, that extended integer type would have to have a lower rank than 'std::uint_least16_t', since it has a smaller width. And so according to the rules for 'make_unsigned', the implementation would have to yield that extended type with the smaller width and the smaller value range before it would yield 'std::uint_least16_t', or some other comparable integer type.
>>>
>>> So what I'm seeking clarification on is, does it actually work this way or have I misunderstood something? And if it does work that way, is it supposed to? I'd be able to work around this in my code if need be, so it's not so big a deal to me, but it has piqued my interest.
>>>
>>> Thanks
>> --
>> Std-Discussion mailing list
>> Std-Discussion_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
On Friday, September 18th, 2026 at 12:06 PM, Jan Schultke <janschultke_at_[hidden]> wrote:
> It is worth mentioning that if P3666 is accepted, then _BitInt will not be an eligible choice for make_signed<char16_t> or make_unsigned<char16_t>, only standard or extended integer types.
>
> See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3666r4.html#meta.trans.sign
>
> On Fri, 18 Sept 2026 at 16:08, Keenan Horrigan via Std-Discussion <std-discussion_at_[hidden]> wrote:
>
>> Perhaps there's a more grounded example available using the _BitInt types, which Clang provides in C++ as extended integer types.
>>
>> The following code successfully compiles today on Clang:
>>
>> __extension__ using uint9_ext_t = unsigned _BitInt(9);
>>
>> static_assert(std::integral<uint9_ext_t>);
>> static_assert(sizeof(uint9_ext_t) == 2);
>> static_assert(std::numeric_limits<uint9_ext_t>::max() == 511);
>>
>> static_assert(std::integral<char16_t>);
>> static_assert(sizeof(char16_t) == 2);
>> static_assert(std::numeric_limits<char16_t>::max() == 65535);
>>
>> static_assert(std::same_as<std::make_unsigned_t<char16_t>, unsigned short>);
>>
>> Godbolt link: https://godbolt.org/z/8a5666rWd
>>
>> According to the 'make_unsigned' rules, 'make_unsigned<char16_t>' should be providing 'uint9_ext_t' here before it would provide the standard integer type 'unsigned short', right? But of course that would be undesirable because 'uint9_ext_t' is more narrow than 'char16_t', despite having the same size and the same sign.
>>
>> On Thursday, September 17th, 2026 at 1:56 AM, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>>
>>> Hello,
>>>
>>> Recently I've been working on some code that needs to deal somewhat finely with integer/integral types, so I've been going and reading the standard to make sure that I'm staying within the bounds of the standard with regards to integer types. Something that caught my eye, and is somewhat meaningful to my code, is the specification of 'std::make_unsigned', which can be seen here: https://eel.is/c++draft/meta.trans.sign
>>>
>>> For signed and unsigned integer types, it works how I would expect, but for other types it's specified as yielding "the unsigned integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type)". And what's confusing to me about that is that it talks about the *size* of the type, rather than the *width* of the type, which I think could hypothetically lead to getting a type which may have the same size as the original type, but which has a smaller width and therefore a smaller value range.
>>>
>>> For instance, we might try to calculate 'make_unsigned_t<char16_t>', and let's assume that we're on a platform where char16_t has a size of two bytes and a width of 16. To my eye, that call to 'make_unsigned' is not required to yield what one might expect, that being the actual underlying type of char16_t, 'std::uint_least16_t', nor even a type that has the same properties. If we were to imagine that our implementation provides an extended unsigned integer type that has a size of two bytes, but which has 8 padding bits and a width of 8, then, if I'm understanding all these rules correctly, that extended integer type would have to have a lower rank than 'std::uint_least16_t', since it has a smaller width. And so according to the rules for 'make_unsigned', the implementation would have to yield that extended type with the smaller width and the smaller value range before it would yield 'std::uint_least16_t', or some other comparable integer type.
>>>
>>> So what I'm seeking clarification on is, does it actually work this way or have I misunderstood something? And if it does work that way, is it supposed to? I'd be able to work around this in my code if need be, so it's not so big a deal to me, but it has piqued my interest.
>>>
>>> Thanks
>> --
>> Std-Discussion mailing list
>> Std-Discussion_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
Received on 2026-09-18 17:21:30
