2

在我正在进行的项目中,我发现自己经常需要调整对象数组的大小,因为新对象被创建并且旧对象被销毁。在整个代码中,许多不同的派生类型都会发生这种情况,其中大多数彼此没有关系。编写代码为唯一的派生类型调整这些数组的大小是很乏味的,所以我想我会尝试使用无限的多态虚拟参数编写几个帮助子例程,以便任何派生类型数组都可以使用这些子例程。

我发现我的无限多态例程可以编译和调用CLASS(*),INTENT(INOUT) :: val。此虚拟参数将接受整数、可分配整数或指向整数的指针。但是,一旦我尝试添加ALLOCATABLEorPOINTER属性,子例程就会正确编译,但我无法在没有获得编译器错误的情况下调用它。由于我的目标是能够调整派生类型数组的大小,因此这些属性对于例程能够解除分配/分配/关联值是必需的。

这是一些测试代码,它们甚至没有尝试实际做任何事情但无法编译。此版本使用标量具体类型,但在使用具体类型数组或派生类型数组的原始代码中也会出现相同的错误

MODULE Resize_mod
PUBLIC
CONTAINS
  SUBROUTINE resize(val)
    CLASS(*),INTENT(INOUT) :: val
    WRITE(*,*) 'resize'
  ENDSUBROUTINE resize
  SUBROUTINE resize_alloc(val)
    CLASS(*),ALLOCATABLE,INTENT(INOUT) :: val
    WRITE(*,*) 'resize_alloc'
  ENDSUBROUTINE resize_alloc
  SUBROUTINE resize_ptr(val)
    CLASS(*),POINTER,INTENT(INOUT) :: val
    WRITE(*,*) 'resize_ptr'
  ENDSUBROUTINE resize_ptr
ENDMODULE Resize_mod

PROGRAM testResize
  USE Resize_mod
  INTEGER,TARGET :: array0d
  INTEGER,ALLOCATABLE :: alloc0d
  INTEGER,POINTER :: ptr0d

  array0d=1
  CALL resize(array0d)

  ALLOCATE(alloc0d)
  alloc0d=1
  CALL resize(alloc0d)
  !Following line gives: "Error: Actual argument to ‘val’ at (1) must be polymorphic"
  CALL resize_alloc(alloc0d)

  ALLOCATE(ptr0d)
  ptr0d=1
  CALL resize(ptr0d)
  !Following line gives: "Error: Actual argument to ‘val’ at (1) must be polymorphic"
  CALL resize_ptr(ptr0d)

ENDPROGRAM testResize

如图所示,代码产生以下错误:

testResize.f90:31:20:

   CALL resize_alloc(alloc0d)
                    1
Error: Actual argument to ‘val’ at (1) must be polymorphic
testResize.f90:37:18:

   CALL resize_ptr(ptr0d)
                  1
Error: Actual argument to ‘val’ at (1) must be polymorphic

如果我注释掉错误中命名的 2 行,我会得到以下正确输出:

 resize
 resize
 resize

我已经广泛编写了 Fortran 代码,但以前从未尝试过使用无限多态性。请让我知道我正在尝试做的事情是否可行,如果可以,我做错了什么。

我正在使用 gfortran 编译器 5.4.0,据我所知,它应该完全支持无限多态性。

4

2 回答 2

2

错误消息只是对语言规则限制的重述 - 请参阅 Fortran 2018 标准中的 15.5.2.5p2。限制是为了阻止被调用的过程将可分配的虚拟参数重新分配给与实际参数不同的类型或种类(或者,对于指针虚拟参数,将虚拟参数与不同的类型或种类相关联)。这并不特定于无限多态参数 - 它适用于任何可分配或指针多态虚拟参数,在这种情况下,语言中的限制阻止过程将虚拟参数分配给类型继承树的不同分支。

无限多态对象在运行时类型不可知存储中发挥作用,但在一般情况下它们不适合泛型编程。

该语言以通用方式为常见的数组操作提供了一些语法支持,但是当前的编译器可能无法有效地实现这些操作。例如,可以使用语法将元素附加到可分配数组array = [ array, element ]

否则,您需要为数组操作提供类型特定的过程。对于相同的操作,无论参数类型如何,每个过程主体内的标记序列通常可以相同,在这种情况下可以使用 INCLUDE 来减少重复源代码的数量。

改进对泛型编程的支持是该语言的下一个修订版正在考虑的一个方面。

于 2019-06-05T21:05:43.727 回答
1

当您声明一个可分配对象时

class(*), allocatable :: obj(:)

不可能为虚拟参数指定“可分配”性质独立地应用于它的数组形状或类型方面。

一旦您说 dummy 参数具有该allocatable属性,则完全限制适用于 dummy 参数和相关的实际参数。在这种情况下,可分配的无限多态伪参数可能仅与可分配的无限多态实际参数相关联。1

实际参数alloc0d是声明类型的integer,并且不是无限多态的。因此不允许这样的程序引用。

对于指针虚拟/实际参数,同样的逻辑明确成立。为了完整起见,如果 dummy 不是无限多态的,那么对实际参数的要求是它必须是相同的声明类型。

如果虚拟参数上没有allocatableorpointer属性,则参数关联是有效的。

为了能够重塑可分配/指针虚拟数组参数,则有必要使虚拟参数不是多态的。您将需要找到另一种方法来处理此类情况(可能使用泛型)。


1这样做的动机是,在过程内部,可分配的多态伪参数可能具有其动态类型,以及它的形状(如果是数组),在allocate语句中发生了变化。如果定义不兼容,该语句显然不会影响相关的实际参数。这又符合 Fortran 的规则:我们不能说“这个对象是多态的,但我保证不会改变它的类型”。

于 2019-06-05T20:55:33.470 回答