为什么将std :: ssize强制为其签名大小类型设置为最小大小?

时间:2019-05-22 03:16:31

标签: c++ language-lawyer c++20

在C ++ 20中,引入了std::ssize以获取用于通用代码的容器的带符号大小。 (其添加的原因已在here中进行了说明。)

有些奇怪的是,给定的定义(与common_typeptr_diff_t结合使用)的作用是强制返回值为“ ptrdiff_t或容器的{ {1}}返回值,以较大者为准。

P1227R1间接为此提供了理由(“ 将std :: ssize()的大小从60,000变为-5,536 将是灾难性的”)。

在我看来,这似乎是尝试“修复”该问题的一种奇怪方法。

  • 有意定义size()大小且已知不超过32,767个元素的容器仍将被迫使用比所需的更大的类型。
    • 同样,对于大小分别为uint16_t和127个元素的容器。
    • 在桌面环境中,您可能不在乎;但这对于嵌入式或其他资源受限的环境可能很重要,尤其是如果结果类型用于比堆栈变量更持久的环境时。
  • 在32位平台上使用默认uint8_t大小但仍包含2B和4B项的容器将遇到与上述完全相同的问题。
  • 如果仍然存在size_t小于32位的平台,它们也会遇到相同的问题。

仅按原样使用带符号的类型(不扩展其大小),并且ptrdiff_t不会发生转换错误(例如,结果不是负数)会更好吗? / p>

我想念什么吗?


稍微扩展一下最后一个建议(受Nicol Bolas' answer的启发):如果按照我建议的方式实施了该代码,那么此代码就是Just Work™:

assert

但是,在当前的实现中,这会产生警告和/或错误,除非明确添加void DoSomething(int16_t i, T const& item); for (int16_t i = 0, len = std::ssize(rng); i < len; ++i) { DoSomething(i, rng[i]); } 来缩小static_cast的结果,或者改为使用ssize然后缩小可以在函数调用(以及范围索引)中找到它,但似乎都没有改善。

1 个答案:

答案 0 :(得分:1)

  

故意定义uint16_t大小且已知不超过32,767个元素的容器仍将被迫使用比所需的更大的类型。

这不是容器将这种类型的尺寸存储在容器中。通过访问值进行转换。

对于嵌入式系统,嵌入式系统程序员已经知道C ++倾向于增加小型类型的大小。因此,如果他们期望类型为int16_t,那么他们将在代码中拼写出来,因为否则C ++可能会将其提升为int

此外,没有标准的方法可以询问“已知永不超过”范围的大小。 decltype(size(range))是您可以要求的;大小范围不需要提供max_size函数。如果没有这种能力,最安全的假设是其大小类型为uint16_t的范围可以假设该范围内的任何大小。因此,带符号的大小应该足够大,可以将整个范围存储为带符号的值。

您的建议基本上是,任何ssize调用都可能是不安全的,因为任何size范围的一半都不能有效地存储在ssize的返回类型中。

  

在32位平台上使用默认size_t大小但仍包含2B和4B项的容器将遇到与上述完全相同的问题。

假设ptrdiff_t在这样的平台上不是有符号的64位整数是有效的,那么实际上没有有效的解决方案。所以是的,在某些情况下ssize可能不安全。

ssize当前在不安全的情况下可能不安全。您的建议将使ssize所有情况下都不安全。

这不是改善。

不,仅断言/合同检查不是可行的解决方案。 ssize的重点是使for(int i = 0; i < std::ssize(rng); ++i)正常工作,而编译器不会抱怨有符号/无符号不匹配。要获得因不需要发生的转换失败而引起的断言(并且顺便说一句,如果不使用std::size,我们将努力避免,则无法纠正),最终与您的算法无关?这是一个可怕的主意。


  

如果按照我建议的方式实施,则此代码将是Just Work™:

让我们忽略用户编写此代码的频率问题。

您的编译器期望/要求您使用强制转换的原因是因为您要进行固有危险的操作:您可能会丢失数据。如果当前大小适合int16_t,则您的代码仅“ Just Works™”;这使得转换具有静态危险。这不是应该隐式发生的事情,因此编译器建议/要求您明确要求它。使用该代码的用户会大眼睛发胖,提醒他们正在做危险的事情。

这很好。

看看,如果您建议的实现是ssize的行为方式,那么这意味着我们必须将ssize的每次使用都视为一样固有的危险,就像编译器对待您的尝试一样隐式转换。但是与static_cast不同,ssize很小,很容易遗漏。

危险操作应被这样指出。由于ssize很小,难以通过设计注意,因此它应该尽可能安全。理想情况下,它应该与size一样安全,但是如果失败,它应该仅在无法使其安全的范围内才是不安全的。

用户不应将ssize的使用视为可疑或令人不安的内容;他们不应该害怕使用它。