Pointers Fortran2003:指向返回多态类型指针的函数的过程指针
对于一个新项目,我正在考虑使用Fortran2003的面向对象功能。我尝试过的一件事涉及一个过程指针,它指向一个函数(不是子例程),该函数返回一个指向多态类型的指针。我想知道这样的构造是否合法,因为我从不同的编译器中得到了不同的结果(见下文)Pointers Fortran2003:指向返回多态类型指针的函数的过程指针,pointers,polymorphism,fortran,fortran2003,Pointers,Polymorphism,Fortran,Fortran2003,对于一个新项目,我正在考虑使用Fortran2003的面向对象功能。我尝试过的一件事涉及一个过程指针,它指向一个函数(不是子例程),该函数返回一个指向多态类型的指针。我想知道这样的构造是否合法,因为我从不同的编译器中得到了不同的结果(见下文) 作为一个具体的例子,考虑下面的函数接口: abstract interface function if_new_test(lbls) result(t) import :: test_t class(test_t),point
作为一个具体的例子,考虑下面的函数接口:
abstract interface
function if_new_test(lbls) result(t)
import :: test_t
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
end function if_new_test
end interface
procedure(if_new_test),pointer :: nt
并且使用的代码应该有一个过程指针,可以指向具有此接口的函数:
abstract interface
function if_new_test(lbls) result(t)
import :: test_t
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
end function if_new_test
end interface
procedure(if_new_test),pointer :: nt
我问这是否合法,因为gfortran(4.7.2)抱怨此过程指针声明,并显示以下消息:
错误:(1)处的类变量“nt”必须是伪的、可分配的或指针
我不理解这个错误消息,因为nt
本身就是一个指针,它指向的函数返回的也是一个指针
下面是示例的完整源代码,仅供参考。首先,包含派生类型、接口和函数/子例程的模块:
module test_m
implicit none
type :: test_t
character(len=10) :: label
contains
procedure :: print => print_test
end type test_t
type,extends(test_t) :: test2_t
character(len=10) :: label2
contains
procedure :: print => print_test2
end type test2_t
abstract interface
function if_new_test(lbls) result(t)
import :: test_t
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
end function if_new_test
subroutine if_make_test(t,lbls)
import :: test_t
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
end subroutine if_make_test
end interface
contains
subroutine print_test(self)
implicit none
class(test_t),intent(in) :: self
print *, self%label
end subroutine print_test
subroutine print_test2(self)
implicit none
class(test2_t),intent(in) :: self
print *, self%label, self%label2
end subroutine print_test2
function new_test(lbls) result(t)
implicit none
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
call make_test(t,lbls)
end function new_test
function new_test2(lbls) result(t)
implicit none
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
call make_test2(t,lbls)
end function new_test2
subroutine make_test(t,lbls)
implicit none
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
allocate(test_t::t)
t%label = lbls(1)
end subroutine make_test
subroutine make_test2(t,lbls)
implicit none
class(test_t),pointer :: t
character(len=*),intent(in) :: lbls(:)
allocate(test2_t::t)
select type(t) ! so the compiler knows the actual type
type is(test2_t)
t%label = lbls(1)
t%label2 = lbls(2)
class default
stop 1
end select
end subroutine make_test2
end module test_m
以及使用此模块的主程序:
program test
use test_m
implicit none
class(test_t),pointer :: p
procedure(if_make_test),pointer :: mt
procedure(if_new_test),pointer :: nt
mt => make_test
call mt(p,["foo"])
call p%print
deallocate(p)
mt => make_test2
call mt(p,["bar","baz"])
call p%print
deallocate(p)
p => new_test(["foo"])
call p%print
deallocate(p)
p => new_test2(["bar","baz"])
call p%print
deallocate(p)
nt => new_test
p => nt(["foo"])
call p%print
deallocate(p)
nt => new_test2
p => nt(["bar","baz"])
call p%print
deallocate(p)
end program test
程序首先通过子例程make_test
和make_test2
创建对象,在我的测试中,这适用于我尝试过的所有编译器。接下来,通过直接调用函数new\u test
和new\u test2
来创建对象,这也适用于我的测试。最后,应该再次通过这些函数创建对象,但间接地通过过程指针nt
如上所述,gfortran(4.7.2)不编译nt
的声明
ifort(12.0.4.191)在nt=>new\u test
行生成内部编译器错误
pgfortran(12.9)编译时不会发出警告,可执行文件会产生预期的结果
那么,根据Fortran2003,我试图做的是非法的,还是编译器对这些特性的支持仍然不够?我应该只使用子例程而不是函数吗(这似乎很有效)?您的代码似乎很好。我可以用Intel 13.0.1和NAG 5.3.1编译它,没有任何问题。较旧的编译器可能在Fortran 2003更“奇特”的功能方面存在问题
根据问题的不同,您还可以使用可分配类型而不是指针。另一方面,应该更能防止内存泄漏,您将无法作为函数的结果返回多态类型:
module test_m
implicit none
type :: test_t
character(len=10) :: label
contains
procedure :: print => print_test
end type test_t
type,extends(test_t) :: test2_t
character(len=10) :: label2
contains
procedure :: print => print_test2
end type test2_t
abstract interface
function if_new_test(lbls) result(t)
import :: test_t
class(test_t), allocatable :: t
character(len=*),intent(in) :: lbls(:)
end function if_new_test
subroutine if_make_test(t,lbls)
import :: test_t
class(test_t), allocatable :: t
character(len=*),intent(in) :: lbls(:)
end subroutine if_make_test
end interface
contains
subroutine print_test(self)
class(test_t), intent(in) :: self
print *, self%label
end subroutine print_test
subroutine print_test2(self)
class(test2_t), intent(in) :: self
print *, self%label, self%label2
end subroutine print_test2
subroutine make_test(t,lbls)
class(test_t), allocatable :: t
character(len=*),intent(in) :: lbls(:)
allocate(test_t::t)
t%label = lbls(1)
end subroutine make_test
subroutine make_test2(t,lbls)
class(test_t), allocatable :: t
character(len=*),intent(in) :: lbls(:)
allocate(test2_t::t)
select type(t) ! so the compiler knows the actual type
type is(test2_t)
t%label = lbls(1)
t%label2 = lbls(2)
class default
stop 1
end select
end subroutine make_test2
end module test_m
program test
use test_m
implicit none
class(test_t), allocatable :: p
procedure(if_make_test), pointer :: mt
mt => make_test
call mt(p, ["foo"])
call p%print
deallocate(p)
mt => make_test2
call mt(p, ["bar","baz"])
call p%print
deallocate(p)
end program test
还有一句话:模块级的隐式none语句由模块过程“继承”,因此您不必在每个子例程中额外发出它。谢谢您的确认。我现在已经用最新的gcc快照对它进行了测试,事实上,它现在编译时没有警告,并产生了预期的结果。“你将无法作为函数的结果返回多态类型”-你是什么意思?您指的是不能在赋值语句中将具有可分配多态结果的函数作为右侧表达式吗?如果是这样的话,
ALLOCATE(lhs,SOURCE=rhs(..)
是F2003中的简单解决方法。是的,的确,我指的是这个。您知道吗,在Fortran 2008中,如果没有“变通方法”,这是否有效?我真的不明白这一点,为什么这种分配如果与指针一起工作,就不应该优先与可分配项一起使用。2008年的标准不再禁止对多态可分配项进行内部分配,因此这应该随着时间的推移而得到支持。毕竟,许多编译器已经支持类似的隐式(重新)分配可分配数组,错误消息肯定是错误的。注意事项-从一个微妙的语法变化的角度来看,返回指针的函数是危险的,这会导致内存泄漏——考虑如果有人在赋值语句的右边使用函数而不是指针赋值会发生什么。F2008(可能)引入了一些与它们作为实际参数使用相关的额外复杂性。除非你有充分的理由,否则要避免。在这里,可分配性更好,特别是当来自F2008的多态分配的支持被广泛使用时。