我正在努力阅读 Barnes 的优秀 Ada 书。这是 11.7 节中链表深度比较的代码示例:
type Cell is
record
Next: access Cell;
Value: Integer;
end record;
function "=" (L, R: access Cell) return Boolean is
begin
if L = null or R = null then -- universal =
return L = R; -- universal = (Line A)
elsif L.Value = R.Value then
return L.Next = R.Next; -- recurses OK (Line B)
else
return False;
end if;
end "=";
Run Code Online (Sandbox Code Playgroud)
我似乎无法理解为什么在 A 行中调用了 Universal_access 类型的运算符“=”(因为偏好规则),但是在 B 行中,调用了用户定义的运算符“=”(这首先使递归成为可能),这一次没有首选操作符“=”的universal_access。
L 和 R 以及 L.Next 和 R.Next 都是相同的匿名类型“访问单元”。为什么“派遣”有差异?它与 L 和 R 是访问参数有关吗?如果是这样,那里的规则是什么?
我尽力在 AARM 中找到任何内容,尤其是第 4.5.2 节,但无法理解。
干杯。
到目前为止,我将总结我的发现(在 Simon Wright 和 G_Zeus 的帮助下)。如果我错了,请纠正我:
根据标准,L = null、R = null、L = R以及L.Next = R.Next每个都应该明确地调用用户定义的运算符 =。universal_access操作员 = 不能在这里介入。
原因:
的操作数L,R,L.Next和R.Next违反的前提ARM 4.5.2(9.1-9.4) ,用于解释=在这些表达式,以平均操作者的=universal_access类型:
前提条件是,两个操作数都不是access Cell指定类型为Cell( check )的访问对象类型 ( ),Cell也没有用户定义的基本相等运算符 ( check ) 使得
Boolean(检查);Cell( check )相同的声明列表中声明;和Cell(两个操作数都是,check)。ARM 8.6(29.1)中universal_access类型运算符 = 的首选规则在此处不适用,因为它需要“两种可接受的解释”。但是由于 4.5.2,类型的运算符 =不是可接受的解释。universal_access
所以别无选择:在所有情况下(甚至L = null)它都必须是用户定义的运算符 =。
@Simon Wright:所以“无界递归”实际上是正确的编译器行为。
@G_Zeus:针对l = r不正确的编译器行为发出歧义错误,编译器应该选择Access_Equal."=".
该示例应正确读取:
...
if Standard."="(L, null) or Standard."="(R, null) then -- universal =
return Standard."="(L, R); -- universal =
elsif L.Value = R.Value then
return L.Next = R.Next; -- recurses OK
...
Run Code Online (Sandbox Code Playgroud)
干杯。
我没有足够的声誉来评论OP,所以我会写一个答案。
有趣的是,我无法在 Gnat 6.1.1 中编译这样的示例(我使用了 Integer 访问,但我怀疑它有任何相关性)。Gnat 一直告诉我,内联“=”的使用在重载“=”和Standard. 所以我尝试:
package body Access_Equal is
function "=" (L,R : access Integer) return Boolean is
begin
return Standard."="(L, R) or L.all = R.all;
end "=";
end Access_Equal;
Run Code Online (Sandbox Code Playgroud)
它似乎成功了。我不能在代码中使用内联“=”,但是,我必须使用完全限定名称:
with Ada.Text_IO; use Ada.Text_IO;
with Access_Equal; use Access_Equal;
procedure Access_Equal_Test is
l : access Integer := new Integer'(1);
r : access Integer := new Integer'(1);
begin
Put_Line(Boolean'Image(Standard."="(l, r))); -- FALSE
Put_Line(Boolean'Image(Access_Equal."="(l, r))); -- TRUE
Put_Line(Boolean'Image(l = r)); -- does not work
end Access_Equal_Test;
Run Code Online (Sandbox Code Playgroud)
注意:使用该Standard包可能会存在可移植性危险,因为似乎不需要定义通用“=”。更多信息请参见此处。