标准 C++ 库中 size_t 的签名变体

Signed variant of size_t in standard C++ library

标准 C++ 中是否有 size_t 的签名变体?表示与 size_t 完全相同的位大小,但有符号。

我当然可以:

#include <type_traits>
using signed_size_t = std::make_signed_t<std::size_t>;

但也许标准库中已经有类似的定义,而不是发明额外的类型名称?

我知道有 ssize_t 和 ptrdiff_t,都签名了。但是根据他们的描述,它们的位大小似乎都与 size_t 不同。但我需要与 size_t 完全相同的位大小但已签名。

std::size_t 的签名版本”(以及 std::ptrdiff_t 的未签名版本)出现在标准中的一个地方:printf 格式说明符 %zu 用于 std::size_t 个对象。 %zd 用于对象,正如 C++ 标准所指的 C 标准所说,“相应的有符号整数类型 [std::size_t]”

std::printf("%zu %zd %td %tu",
    std::size_t{0}, std::make_signed_t<std::size_t>{0},
    std::ptrdiff_t{0}, std::make_unsigned_t<std::ptrdiff_t>{0}
);

并且由于没有专门为 %zd%tu 命名的类型,我倾向于相信没有像您想要的标准名称(除了 std::make_signed_t<std::size_t>) .


顺便说一句,没有太多理由需要 std::size_t 的有符号变体:std::size_t 用于对象的大小,而对象的大小没有符号。

ssize_t 只能保证保持 -1 或非负值。它的保证范围是 [-1, SSIZE_MAX](并且是 POSIX 特定类型,而不是标准 C++ 类型)。这是因为它用于“无符号值或出错时为 -1”。

C++ 标准库为此仅使用 std::size_t,用 std::size_t(-1) == SIZE_MAX 代替表示 error/special 值(参见:std::basic_string<...>::nposstd::dynamic_extent) ,所以如果你想要一个错误值(或者可能是 std::optional<std::size_t>

,你可以只使用 std::size_t 而不是 ssize_t

如果您想要“表示尺寸但已签名的东西”,std::ssize(c)(“已签名 size”)returns std::common_type_t<std::ptrdiff_t, std::make_signed_t<decltype(c.size())>>。对于数组类型,std::ssize returns std::ptrdiff_t。因此可能为此目的使用 std::ptrdiff_t


如果你想要“用来表示两个迭代器之间距离的类型”(包括指针),std::ptrdiff_t就是为此而生的。这主要与有符号大小的概念一致,std::iterator_traits<...>::difference_type 通常是 std::ptrdiff_t


这些并不代表sizeof(std::ptrdiff_t) == sizeof(std::size_t)。该标准没有定义它们之间的任何关系。 sizeof(std::ptrdiff_t) < sizeof(std::size_t)sizeof(std::ptrdiff_t) > sizeof(std::size_t) 在理论上似乎都是可行的,但我还没有发现任何系统都是这种情况。所以一个简单的断言应该适用于所有平台,并允许你只使用 std::ptrdiff_t:

static_assert(
    sizeof(std::size_t) == sizeof(std::ptrdiff_t) &&
    static_cast<std::size_t>(std::numeric_limits<std::ptrdiff_t>::max()) == std::numeric_limits<std::size_t>::max() / 2u,
    "ptrdiff_t and size_t are not compatible"
);

(有很多系统 std::size_tunsigned int 并且 std::ptrdiff_tsigned long 但是 sizeof(int) == sizeof(long),所以我们必须检查范围类型而不是 std::is_same_v<std::ptrdiff_t, std::make_signed_t<std::size_t>>)

或者像您已有的那样使用 std::make_signed_t<std::size_t>