在C ++ 20中,引入了std::ssize以获取用于通用代码的容器的带符号大小。 (其添加的原因已在here中进行了说明。)
有些奇怪的是,给定的定义(与common_type
和ptr_diff_t
结合使用)的作用是强制返回值为“ ptrdiff_t
或容器的{ {1}}返回值,以较大者为准。
P1227R1间接为此提供了理由(“ 将std :: ssize()的大小从60,000变为-5,536 将是灾难性的”)。
在我看来,这似乎是尝试“修复”该问题的一种奇怪方法。
size()
大小且已知不超过32,767个元素的容器仍将被迫使用比所需的更大的类型。
uint16_t
和127个元素的容器。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
然后缩小可以在函数调用(以及范围索引)中找到它,但似乎都没有改善。
答案 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
的使用视为可疑或令人不安的内容;他们不应该害怕使用它。