Ada:创建一种通用实例化(例如 Vectors)

Ada: Create a type of a generic instantiation (e.g. Vectors)

我正在尝试创建一个类型 'Path',它是一个点向量:

with Ada.Containers.Vectors;
use Ada.Containers;
package Geometry is

    type Point is record
        North : Integer := 0; -- millimetres
        East  : Integer := 0;
    end record;
    function Same_Point (A, B : Point) return Boolean is
      (A.North = B.North and then A.East = B.East);

    package Path_Points is new Vectors
      (Element_Type => Point,
       Index_Type   => Positive,
       "="          => Same_Point);
    use Path_Points;
    -- works, but poor readability. 'result' is a 'path', not a vector
    Result : Vector; 
    
    -- What I'd like to do, a type called 'Path':
    type Path is new Path_Points.Vector; -- WRONG

end Geometry;

这样当我使用 Geometry 时,我可以写

    Result : Path; -- Easy to understand!

如何创建类型 'Path',它只是 Ada.Containers.Vectors.Vector 的别名?

我意识到我可以创建 OOP 风格的 'Private Type Path' 并实现所有管道以复制 Vectors 函数,但它看起来非常笨拙。

Vector 类型是 tagged 类型,这意味着您需要类型扩展而不仅仅是派生:

type Path is new Path_Points.Vector with null record;

我猜编译器在错误消息中暗示了这一点?

编辑:在下面的评论拖到更长的讨论之前,让我澄清一些困惑:

类型在 Ada 中没有名称,只有子类型有,所以类型声明(包括类型派生,创建新类型) 实际上是一个匿名类型的声明,连同它的 first subtype.

派生类型继承基类型的基本操作,就好像它们是用新类型声明的一样。 它们仅通过显式类型转换与基类型兼容,因此您不能调用这些操作 基类型与派生类型作为参数而无需转换。很少需要那个, 但是,因为操作是继承的。

另一方面,子类型作为基本类型的重命名(它们也可以有约束,但这不在讨论范围内), 并且不继承原始操作,但必须依赖基类型进行操作。另一方面,他们是 与基本类型兼容(无需类型转换)。

因此

type Foo is null record;
procedure Process(Item : in out Foo);

概念上是:

type @anonymous_type is null record;
procedure Process(Item : in out @anonymous_type);

subtype Foo is @anonymous_type; 

type Bar is new Foo;

概念上是:

type @derived_type is new @anonymous_type;
procedure Process(Item : in out @derived_type); -- implicit declaration of inherited subprogram

subtype Bar is @derived_type;

subtype Baz is Foo; 

就是这样。一个新名字。

当类型在外部派生或子类型化时,所有这些的实际意义都会发挥作用 他们自己的范围,

with Ada.Strings.Unbounded;
package Foo is
   subtype Bar is Ada.Strings.Unbounded.Unbounded_String;
   -- Bar is a subtype; no inheritance, must call operations on the base type
   Foobar : constant Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobar");
  
   type Baz is new Ada.Strings.Unbounded.Unbounded_String;
   -- Baz is a derived type, inherited operations are directly visible in its scope
   Foobaz : constant Baz := To_Unbounded_String("Foobaz");


   -- However:
  
   -- This is not legal for a subtype (without a `use` clause):
   -- Foobar : constant Bar := To_Unbounded_String("Foobar");


   -- This is not legal for a derived type (without an explicit type conversion):
   --  Foobaz : constant Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobaz");
end Foo;

现在很清楚,派生类型可以显着减少包内部所需的类型(即使没有 use 子句)。

同样,派生类型可以减少所需的类型,以及 API:

的任何用户所需的依赖项(以及可能的 use 子句)的数量

注意子类型如何强制用户with基类型的范围

with Foo;
with Ada.Strings.Unbounded;
procedure Bar_Test is
   Bar : Foo.Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobar");

   -- illegal for a subtype
   -- Bar : Foo.Bar := Foo.To_Unbounded_String("Foobar");
begin
   null;
end Bar_Test;

派生类型不存在的地方

with Foo; -- Baz is a derived type, inherited operations are visible in Foo
procedure Baz_Test is
   Baz : Foo.Baz := Foo.To_Unbounded_String("Foobaz");

   -- illegal for a derived type, type conversion needed
   -- Baz : Foo.Baz := Ada.Strings.Unbounded.To_Unbounded_String("Foobaz");
begin
   null;
end Baz_Test;

类型扩展被定义为派生类型的一种特殊情况,其中基本类型是标记记录类型, 使我们能够扩展包含更多成员的记录:

type Foo is tagged
record
   M1 : Natural;
end record;

procedure Process(Item : in out Foo);


type Bar is new Foo with
record
   M2 : Natural; -- Bar has both members M1 and M2
end record;

-- implicit declaration of Process for Bar, unless Process is overridden (as for Baz below)


type Baz is new Foo with null record; -- no new members, Baz has only M1

overriding
procedure Process(Item : in out Baz); -- Process is overridden 

overriding 关键字是可选的,但建议使用。 还有一个类似的not overriding来避免意外覆盖

标记类型和类型扩展还可以在 class 类型

上启用动态调度
Dispatch : Foo'Class := Baz'(Foo with M1 => 1337); 

Foo'Class 称为 class 宽类型,可以包含 Foo 或任何以 Foo 为根的类型(derived/extended 来自 Foo)

它们还启用了更高级的功能,例如 interface 形式的多重继承, 子程序的前缀视图(更好地称为 Object.Dot 符号)、受控类型等