为什么C++20中引入了std::ssize()函数?

120

C++20引入了以下std::ssize()自由函数:

template <class C>
    constexpr auto ssize(const C& c)
        -> std::common_type_t<std::ptrdiff_t,
                              std::make_signed_t<decltype(c.size())>>;

可能的实现似乎使用static_cast,将类Csize()成员函数的返回值转换为它的有符号对应项。

由于C的size()成员函数总是返回非负值,为什么会有人想要将它们存储在有符号变量中呢?如果确实需要,那么只需要进行简单的static_cast即可。

为什么在C++20中引入了std::ssize()


4
@Jarod42这里应该是实现定义而非未定义吧?(有符号溢出是未定义的,但有符号转换是实现定义的) - phön
13
如果他们也加入ssizeof操作符就好了。 - geza
13
冒昧提出一点看法:我认为C++关于整数类型的整个类型系统有些问题。当然,可以说一些怪癖(比如不知道char有多少位)是从C语言继承而来,至少通过(u)intX_t有所缓解,但仍然存在无穷无尽的微妙却又关键性的错误源。像 ssize 这样的东西只是临时补救措施,而且需要花费一定的时间(也许是“永远”)才能深入人心成为常见的“最佳实践指南”,并被人们严格遵循。 - Marco13
8
另一方面,C/C++类型系统(与例如Java的固定类型系统相比),除了允许C/C++代码在其他大多数语言崩溃的架构上运行之外,确实允许有能力的教师向学生灌输一些重要的课程内容。例如,不是全世界都是64位的。而且,并非全世界都使用8位字符。这些事情很容易应对,并且如果只有教师从一开始就教授这些内容,那么你将成为一名更好的开发人员。 (并且,只是为了确保,你确实知道(u)intX_t类型是可选的,对吗?) - DevSolar
7
我不想在“抨击语言”这条路上走得太远,但坦率地说:拥有大约70种不同的整数类型,其中一些是可选的,并且不知道大多数类型的大小,不能以“教给学生一个教训”的理由来证明。这在语言的定义和规范层面上是过度的(我也不会因为了解所有怪癖而感到自豪)。再次强调,我了解一些历史原因,但没有必要掩饰:这是遗留问题,使应用程序开发人员的生活困难,并导致错误。 - Marco13
显示剩余9条评论
2个回答

88

这篇论文中描述了该理念。引用如下:

当span被纳入C++17时,它使用有符号整数作为索引和大小。部分原因是为了允许使用“-1”作为哨兵值来指示编译时不知道大小的类型。但是,拥有size()返回有符号值的STL容器是有问题的,因此引入了P1089来“修复”这个问题。它获得了大多数支持,但没有达到一致性所需的2比1的投票比例。

这篇论文P1227是一个提案,旨在添加非成员std::ssize和成员ssize()函数。包含它们会使某些代码更加简单,并允许避免在大小计算中出现不需要的无符号性。想法是如果为所有容器都提供ssize(),包括std::ssize()和成员函数,则针对P1089的抵抗力将会降低。


36
for(int i = 0; i < container.ssize() - 1; ++i)这个例子也相当有说服力。 - Caleth
9
@John,我认为他们可以像string::npos一样,使用size_t(-1)作为特殊值来完成相同的事情。 - rubenvb
17
STL中size类型为无符号数一直被认为是一个错误。现在,不幸的是,改正已经太晚了。目前我们能做的最好方式是提供一个自由函数。 - L. F.
17
@L.F.: 在一次会议上,Herb Sutter说过这句话(也可能是Bjarne说的)。不过,他有一点错误。现在,使用32位/64位的计算机,有符号整数大小会更好(所以他是正确的)。但在旧时代(16位大小),有符号整数大小会很糟糕(例如,我们只能分配32k字节的数组)。 - geza
显示剩余8条评论

61

以下内容是从Eric Niebler那里免费借来的:

当STL首次设计时,“无符号类型表示负索引/大小不合理”是普遍接受的智慧。但是,逻辑上,事物的计数不一定是正数。我可能想要在有符号整数中保留计数,以表示添加到集合中或从集合中删除的元素数量。然后我会想将其与集合的大小相结合。如果集合的大小为无符号数,则现在我被迫混合使用有符号和无符号算术,这是一个错误的农场。编译器会警告此问题,但由于STL的设计几乎强制程序员陷入这种情况,因此警告是如此常见,以至于大多数人都关闭了它。这很遗憾,因为这隐藏了真正的错误。

在接口中使用无符号整数并不像许多人认为的那样好处多多。如果用户无意中向API传递略微负数,则它突然变成了巨大的正数。如果API将数字作为有符号数接收,则可以通过断言数字大于或等于零来检测到该情况。

如果我们将无符号整数的使用限制在位操作(例如掩码)中,并在其他所有地方使用有符号整数,则错误发生的可能性较小,并且当出现错误时更容易检测。


7
尽管Swift没有关于负有符号数被重新解释为极大无符号数的担忧(因为没有隐式转换,这才是让你陷入这个疯狂的乐园的真正原因),但它采用了这种方法。他们认为(机器字大小的)'Int'应该是整数的通用货币类型,即使只有正数有意义(比如索引数组)。任何偏离这个规则的都应该有充分的理由。不需要在每个地方担心强制转换,这很好。 - Alexander
4
@JohnZ.Li 实际上,"unsigned int 对于 Java 是有害的"。 (原文链接: https://www.nayuki.io/page/unsigned-int-considered-harmful-for-java) - Nayuki
3
Rust(最现代的系统编程语言)也使用无符号类型作为索引(因为这是有意义的)。C++ 是无法修复的。它是一种老技术,设计不良,几乎所有默认行为都是错误/不直观的。在过去的几个版本中,C++ 所发生的一切都是对糟糕补丁和设计不良功能的修补。 - There is nothing we can do
如果我们将无符号整数的使用限制在位操作(例如掩码)中,并在其他地方使用有符号整数,则错误发生的可能性较小,并且一旦出现错误,更容易检测到。这就是正确的做法 :-) - jose.angel.jimenez

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