具有可分配或指针属性的无限多态虚拟参数



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

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

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

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,据我所知,它应该完全支持无限多态性。

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

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

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

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

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

当你有一个可分配的对象声明如下

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

无法为虚拟参数指定"可分配"性质独立地应用于其数组形状或类型方面。

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

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

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

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

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


1这样做的动机是,在过程中,可分配的多态虚拟参数可能在allocate语句中更改了其动态类型以及形状(如果是数组)。 如果未兼容地定义该语句,则该语句显然不会影响关联的实际参数。 这又回到了Fortran的规则:我们不能说"这个对象是多态的,但我保证不会改变它的类型"。

最新更新