在我正在从事的项目中,我发现自己经常需要调整对象数组的大小,因为要创建新对象并销毁旧对象。在整个代码中,有许多不同的派生类型会发生这种情况,其中大多数彼此之间没有关系。编写代码来调整这些数组的大小以使其具有唯一的派生类型非常繁琐,所以我想我会尝试使用无限的多态伪参数来编写几个辅助子例程,以便任何派生类型数组都可以使用这些子例程。
我发现我的无限多态例程可以用CLASS(*),INTENT(INOUT) :: val
进行编译和调用。该伪参数将接受整数,可分配整数或指向整数的指针。但是,一旦我尝试添加ALLOCATABLE
或POINTER
属性,该子例程就可以正确编译,但是我无法在没有获得编译器错误的情况下调用它。由于我的目标是能够调整派生类型的数组的大小,因此这些属性对于例程能够取消分配/分配/关联值是必需的。
有些测试代码甚至没有尝试执行任何操作,但是编译失败。此版本使用标量具体类型,但在原始代码中使用具体类型数组或派生类型数组也会出现相同的错误
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,据我所知,它应该完全支持无限多态。
答案 0 :(得分:2)
该错误消息仅是对语言规则的限制的重新声明-请参见Fortran 2018标准中的15.5.2.5p2。限制是阻止被调用过程将可分配的虚拟参数重新分配给与实际参数不同的类型或种类(或者,对于指针虚拟参数,将虚拟对象与其他类型或种类相关联)。这并非特定于无限多态参数-它适用于任何可分配或指针多态哑参数,在这种情况下,语言上的限制阻止了过程将哑参数分配给类型继承树的另一个分支。
无限多态对象可以在运行时类型不可知存储中发挥作用,但是在一般情况下它们不适合通用编程。
该语言以通用方式为常见数组操作提供了一些语法支持,但是当前编译器可能无法有效地实现这些语法支持。例如,可以使用语法array = [ array, element ]
将元素附加到可分配数组。
否则,您需要为数组操作提供特定于类型的过程。对于同一操作,无论参数类型如何,每个过程主体内的令牌序列通常可以相同,在这种情况下,可以使用INCLUDE减少重复的源代码量。
对通用编程的支持得到改进,这是该语言的下一个修订版正在考虑的一个方面。
答案 1 :(得分:1)
当您有一个类似的可声明对象
class(*), allocatable :: obj(:)
不可能为哑元参数指定“可分配”性质独立地应用于其数组形状或类型方面。
只要您说哑元参数具有allocatable
属性,就完全适用于哑元参数和关联的实际参数。在这种情况下,可分配的无限多态虚拟参数只能与可分配的无限多态虚拟参数关联。 1
实际参数alloc0d
的声明类型为integer
,并且不是无限多态的。因此,不允许这样的过程引用。
相同的逻辑对于指针虚拟/实际参数明确地成立。为了完整起见,如果虚拟对象不是无限多态的,那么实际参数的要求是它必须具有相同的声明类型。
在虚拟参数上没有allocatable
或pointer
属性的情况下,参数关联是有效的。
为了能够重塑可分配/指针的伪数组参数,必须使伪参数不具有多态性。您将需要找到另一种方式来处理此类情况(也许使用泛型)。
1 这样做的动机是,在过程内部,可分配的多态伪参数可能会在allocate
中更改其动态类型及其形状(如果是数组)声明。如果没有正确定义,则该语句显然不会影响关联的实际参数。这再次符合Fortran的规则:我们不能说“该对象是多态的,但我保证不会更改其类型”。