C++标准库中的有符号size_t变量

7
在标准 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一样,但是带符号。


2
但我需要与size_t完全相同的位大小,但是带符号。为此,请使用static_assertstatic_assert(sizeof(std::size_t) == sizeof(std::ptrdiff_t), "error message");。这是我所知道的最好方法。 - NathanOliver
@NathanOliver 所以通常 ptrdiff_t 可以用作有符号的 size_t,但你必须检查以确保吗? - Arty
6
ssize_t 不是标准的 C++ 类型,而是 POSIX 的类型。但是,如果你需要一个有符号的 size_t 版本,那么你的方法似乎可行。我猜测没有标准的类型是因为负数的 "大小" 实际上并不存在,因此标准更喜欢使用类似 ptrdiff_t 这样更有意义的名称来表示需要这样的东西。 - HTNW
1
std::ssize,但它可能比size_t更大。 - user975989
@user975989 我认为你链接的是错误的页面,那是函数页面。无论如何,保留这两种类型可能是值得的。如果您获得了更大的 ssize,那么也会有相应的 std::make_unsigned。哦,在这个问题上,是否有人确切地知道“与 T 对应的有符号整数类型”根据 std::make_signed 是否保证大小相同,或者是否还有其他逻辑?std::make_unsigned 的页面很具体,但仅涉及枚举。 - John P
显示剩余2条评论
1个回答

6

有一个地方在标准中用到了“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` 用于对象的大小,而对象的大小不是有符号的。
`ssize_t` 只保证保存 `-1` 或非负值。它保证的范围是 `[-1, SSIZE_MAX]`(这是 POSIX 特定类型,不是标准 C++ 类型)。这是因为它用于“无符号值或错误时为-1”。
C++ 标准库只使用 `std::size_t` 来表示这一点,而使用 `std::size_t(-1) == SIZE_MAX` 来表示错误/特殊值(参见:`std::basic_string<...>::npos`,`std::dynamic_extent`),因此,如果您需要错误值(或者可能是 `std::optional`),则可以直接使用 `std::size_t`,而不是 `ssize_t`。
如果您需要“表示大小但带符号性质的东西”,则 `std::ssize(c)` (“signed size”)返回 `std::common_type_t>`。对于数组类型,`std::ssize` 返回 `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)` 都似乎理论上是可能的,但我没有找到任何这样的系统。因此,在所有平台上都可以使用简单的断言并允许您只使用 `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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接