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