为什么 numeric_limits<uint16_t>::max() 不等于 -1?

Why is numeric_limits<uint16_t>::max() not equal to -1?

#include <iostream>
#include <cstdint>

using namespace std;

static_assert(-1 == numeric_limits<uint64_t>::max()); // ok
static_assert(-1 == numeric_limits<uint32_t>::max()); // ok
static_assert(-1 == numeric_limits<uint16_t>::max()); // error

int main()
{
    cout << numeric_limits<uint16_t>::max() << endl;
    cout << uint16_t(-1) << endl;
}

输出:

65535
65535

为什么numeric_limits<uint16_t>::max()不等于-1?

更新:

根据cppref

Similarly USHRT_MAX may not be of an unsigned type: its type may be int.

因为 -1 未转换为 uint16_t

std::numeric_limits<std::uint16_t>::max() 升级为 int-1 != 65535.

整数转化和促销

uint16_t 值经过 整数提升 而对于(您的特定平台;见下文)uint32_tuint64_t 情况-1 value(本身不是整数文字,而是应用于整数文字 1 的一元减号运算符),经过 integer由于此转换的源值和目标值之间的整数一致,转换 的结果值等于 uint32_tuint64_t 类型的最大值。 =94=]

static_assert(-1 == std::numeric_limits<std::uint64_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint64_t
//            |   Resulting value: std::numeric_limits<std::uint64_t>::max()

static_assert(-1 == std::numeric_limits<std::uint32_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint32_t
//            |   Resulting value: std::numeric_limits<std::uint32_t>::max()

static_assert(-1 == std::numeric_limits<std::uint16_t>::max());
//                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                  | Integer promotion:
//                  |   Destination type: int
//                  |   Resulting value: std::numeric_limits<std::uint16_t>::max()

来自 [expr.eq]/1 and [expr.eq]/6 [强调 我的]:

[expr.eq]/1

The == (equal to) and the != (not equal to) operators group left-to-right. The operands shall have arithmetic, enumeration, pointer, or pointer to member type, or type std​::​nullptr_­t. The operators == and != both yield true or false, i.e., a result of type bool. In each case below, the operands shall have the same type after the specified conversions have been applied.

[expr.eq]/6

If both operands are of arithmetic** or enumeration type, the usual arithmetic conversions are performed on both operands; each of the operators shall yield true if the specified relationship is true and false if it is false.

来自 [conv.integral]/1 and [conv.integral]/2:

[conv.integral]/1

A prvalue of an integer type can be converted to a prvalue of another integer type. A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type.

[conv.integral]/2

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation).  — end note ]

仅此一项就应该为所有三个示例产生相同的行为。但是,uint16_t 案例的不同之处在于 [conv.integral]/5 适用:

[conv.integral]/5

The conversions allowed as integral promotions are excluded from the set of integral conversions.

来自 [conv.rank]/1

[conv.rank]/1

Every integer type has an integer conversion rank defined as follows:

[...]

(1.3) The rank of long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.

(1.4) The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type.

uint16_t的整数转换等级(与short int相同等级或低于short int)低于int,即[conv.prom]/1申请uint16_t [强调我的]:

[conv.prom]/1

A prvalue of an integer type other than bool, char16_­t, char32_­t, or wchar_­t whose integer conversion rank is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.


平台相关行为

但是,虽然由于最大值[=139]的下限要求,我们能够对上面的uint16_t进行论证=] 的 unsigned short int——保证 uint16_t 总是比 int 具有更低的整数转换等级——我们不能对 uint32_t 将 [=120] 的事实进行相反的论证=]永远不会 整数转换等级 低于 int,因为 ISO C++ 标准没有 最大值 对基本整数类型的要求。

来自 [basic.fundamental]/2 and [basic.fundamental]/3 [摘录,强调 我的]:

[basic.fundamental]/2

There are five standard signed integer types : “signed char”, “short int”, “int”, “long int”, and “long long int”. In this list, each type provides at least as much storage as those preceding it in the list. [...] Plain ints have the natural size suggested by the architecture of the execution environment; the other signed integer types are provided to meet special needs.

[basic.fundamental]/3

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: “unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”, each of which occupies the same amount of storage and has the same alignment requirements as the corresponding signed integer type; [...]

The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1.

并且,从 C11 Standard draft [摘录,强调 我的]:

5.2.4.2.1 Sizes of integer types <limits.h>

[...] Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.

[...]

  • maximum value for an object of type short int: SHRT_MAX +32767

  • maximum value for an object of type int: INT_MAX +32767

[...]

请注意,这些最大值描述了相应基本整数类型应能够存储的最大值的下限,而对这些最大值的上限没有要求。此外,回想一下上面的 [basic.fundamental]/2 引用,每个后续的基本(有符号)整数类型只需要提供 至少 与处理它的那个一样多的存储空间(在名单)。

这意味着,理论上,一个平台可以将 short intint 分别实现为 32 位宽和 64 位宽的整数,这意味着,在这个平台上,uint32_t 将具有与 (unsigned) short int 相同的整数转换等级,这意味着 低于 转换等级 int,在这种情况下 [conv.prom]/1 也适用于 uint32_t 示例 在此特定平台上 .