为匿名访问类型重载运算符“=”?

dac*_*ker 5 ada

我正在努力阅读 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 节,但无法理解。

干杯。

dac*_*ker 5

到目前为止,我将总结我的发现(在 Simon Wright 和 G_Zeus 的帮助下)。如果我错了,请纠正我:

根据标准,L = nullR = nullL = R以及L.Next = R.Next每个都应该明确地调用用户定义的运算符 =。universal_access操作员 = 不能在这里介入。

原因:

的操作数LRL.NextR.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)

干杯。


G_Z*_*eus 1

我没有足够的声誉来评论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可能会存在可移植性危险,因为似乎不需要定义通用“=”。更多信息请参见此处