Fortran 2003 中的类型绑定过程重载



我已经用Java编程几年了;但是,我现在正在学习一门使用Fortran作为示例代码(77标准)的课程。 虽然我一直认为Fortran是一种古老的语言,但我决定使用gfortran编译器尝试2003标准的最新实现,亲眼看看它的优点。 到目前为止,我对现代功能感到惊讶,但我遇到了一个问题,下面的示例演示了这个问题。

module mod1
type type1
real :: x
real :: y
contains
procedure :: compute
end type type1
contains
subroutine compute(this)
class(type1) :: this
this%y = this%x*2 - 1
write (*,*) this%x,this%y
end subroutine
end module mod1
module mod2
type type2
real :: x
real :: y
contains
procedure :: compute
end type type2 
contains
subroutine compute(this)
class(type2) :: this
this%y = this%x - 5
write (*,*) this%x,this%y
end subroutine
end module mod2
program test
use mod1
use mod2
implicit none
type(type1) myType1
type(type2) myType2
myType1%x = 4
myType2%x = 5
call myType1%compute
call myType2%compute
end program test

这会产生编译错误:"在 (1) 处的参数'this'中键入不匹配;将 TYPE(type2) 传递给 CLASS(type1)",以引用call myType2%compute语句。

我的问题是范围问题。 似乎通过class(<class_name>) :: this语句,编译器应该能够将子例程绑定到特定的派生类型或其后代。 从这里开始,编译器从概念上来说,从子例程中本地开始搜索变量定义,然后沿着this特定实例的祖先树向上搜索似乎并不困难。 这将消除所有显式this%语句,这些语句往往会使我的类型绑定过程在几个语句后难以阅读。 例如

this%tempNew(xI) = this%lamda*this%temp(xI-1)+(1-2*this%lamda)*this%temp(xI)+this%lamda*this%temp(xI+1)

似乎比可读/写性低得多

tempNew(xI) = lamda*temp(xI-1)+(1-2*lamda)*temp(xI)+lamda*temp(xI+1)

在后一种情况下,通过class(<class_name>) :: this语句相当明显,其中每个变量都应该绑定。

另一个结果是,两个单独的派生类型似乎不能具有同名的绑定子例程(如错误消息所示)。 我已经看到了两种常见的解决方法。 首先是显式调用每个子例程,例如compute_type1compute_type2。 当访问这些子例程时,这在代码中看起来非常丑陋和冗余。 例如call myType1%compute_type1. 第二个选项(例如,请参阅具有不同等级的重载 fortran 接口,Fortran 2003 中的类型绑定函数重载)似乎更好,是区分绑定名称和过程名称。 例如,类型定义将包括procedure :: compute type => compute_type1. 这解决了访问子例程时的问题,但是在开发具有许多实现相同绑定名称的派生类型的大型项目时,我可以看到问题。 我宁愿不必跟踪我在任何给定项目中拥有和未使用过的子例程名称。 这往往会使名称保持相当长并且最终可读性较低。

所以我的问题有 3 个组成部分:

  • 在类型绑定过程中为类成员显式键入this%<var_name>之外,是否有更干净的替代方法?
  • 有没有办法让编译器认识到过程应该基于class(<class_name>) :: this语句绑定? 当前重载子例程/函数名称的方法似乎是 90/95 标准的产物,该标准不允许将这些绑定到类型。
  • 如果没有,此实现是否有一些性能提升? 这两个问题似乎都可以在编译时解决,我很乐意为了提高表达能力而牺牲它。

这更像是一个扩展的评论,但似乎由于编译器失败,您被迫进行推测。 我已经使用 gfortran 4.8.3 编译了代码,没有错误,并且得到了预期的结果。 另外,在我看来,你想要发生的事情应该发生。

另一个结果是,两个单独的派生类型似乎不能具有同名的绑定子例程(如错误消息所示)。

虽然这两个子例程都被称为compute但它们位于单独的模块中,这是允许的,尽管您的use语句使call compute(...)(您没有这样做)不明确。 如果类型定义在同一个模块中,那么您将不得不求助于procedure :: compute => compute_typex技巧,但这仍然是可以接受的call mytype1%compute

我建议,如果您通过类型绑定公开compute子例程,则将它们作为模块中的private,或者至少不要显式use它们。 [也就是说,有use mod1, only : type1

至于你是否在子例程中坚持type%...,那么是的,我认为你是。 也就是说,在Fortran 2008下,有associate结构

subroutine compute(this)
class(type1), intent(inout) :: this
associate (x => this%x, y => this%y)
...
end associate
end subroutine

但在这种情况下,这并没有多大收获。 还有其他可怕的技巧,我不会详细介绍。

最新更新