为什么 Ada 不捕获这个指定的范围检查?

Why is Ada not trapping this specified range check?

我正在阅读 learn.adacore.com 教程并遇到了一个我不确定的问题。

具体来说,我了解到 Ada 旨在捕获试图溢出具有指定范围定义的变量的尝试。

在下面的例子中,第一次尝试这样做会导致编译器 'range check failure',这是预期的。但是下面一行并没有捕获它,我不确定为什么:

with Ada.Text_IO; use Ada.Text_IO;

procedure Custom_Floating_Types is
    type T_Norm is new float range -1.0 .. 1.0;

    D : T_Norm := 1.0;
begin
    Put_Line("The value of D =" & T_Norm'Image(D));
    -- D := D + 1.0; -- This causes a range check failure at run time = completely expected.
    Put_Line("The value of D =" & T_Norm'Image(D + 1.0)); -- This doesn't?

end Custom_Floating_Types;

因为您没有在 D 中存储大于范围的值。

'图片的定义说:

For every scalar subtype S:

S'Image denotes a function with the following specification:

function S'Image(Arg : S'Base) return String

如您所见,它采用基本(无约束)类型的参数

T_Norm'Image(D + 1.0) 既不分配也不读取超出范围的值。它要求(D + 1.0)的'图像属性(字符串表示),这与要求(1.0 + 1.0)相同。

我可以看到两种可能引起混淆的方式。首先,“属性”这个名称可能会被误解为暗示“图像涉及 D 的某些内在因素。但事实并非如此。 'Image 属性只是一个函数,因此 D 只是定义参数值的表达式的一部分(在您的示例中 = 2.0)。

其次,'Image属性来源于Float。因此,任何 Float 都可以作为它的参数。

您可以创建一个仅接受 T_Norm'范围参数的函数,并使该函数成为 T_Norm 的属性。参见 Ada 参考手册 4.10。

你有几个很好的答案,但我要添加另一个,因为很明显你希望表达式 D + 1.0 引发异常,而你的答案没有解释为什么它没有。

这样的类型声明
type T_Norm is new float range -1.0 .. 1.0;

大致相当于

type T_Norm'Base is new Float;
subtype T_Norm is T_Norm'Base range -1.0 .. 1.0;

类型(称为“基本类型”)没有给出名称,但通常可以使用 'Base 属性引用它。给定的名称是一个子类型,称为“第一个命名的子类型”。

这个区别很重要,但往往没有得到足够的重视。正如 egilhh 所解释的,T_Norm'Image 是根据 T_Norm'Base 定义的。算术运算符也是如此。例如,"+" 定义为

function "+" (Left : in T_Norm'Base; Right : in T_Norm'Base) return T_Norm'Base;

2.0 显然在 T_Norm'Base 的范围内,因此评估 D + 1.0 没有违反任何约束,也没有将其传递给 T_Norm'Image。但是,当您尝试将结果值分配给具有子类型 T_NormD 时,会检查该值是否在子类型的范围内,并且会引发异常,因为检查失败.

在其他地方使用这种区分是为了使语言合理地工作。比如约束数组类型

type A is array (1 .. 10) of C;

大致相当于

type A'Base is array (Integer range <>) of C;
subtype A is A'Base (1 .. 10);

如果你这样做

V : A;
... V (2 .. 4)

您可能会遇到问题,因为切片没有 A 的边界。但它有效,因为切片没有子类型 A 而是匿名子类型 A'Base (2 ..4).