【问题标题】:Fortran : generic procedure of parent is called instead of child when allocating child from class of parentFortran:从父类分配子类时,调用父类的通用过程而不是子类
【发布时间】:2014-10-13 13:21:53
【问题描述】:

我以例子的形式解释我的问题。

我有一个类型 (location2d_t),其中包括两个成员 xy 和一个类型绑定过程 (calcdist2d)。除了具有 (location2d_t) 类的 (this) 之外,该过程还接受其自己的类型(作为第二个虚拟参数)来计算距离。 现在,我更进一步,将类型扩展为 (location3d_t),它也具有 z

要重新定义过程,我无法覆盖前一个过程,因此我创建了一个新过程 (calcdist3d),第二个参数类型为 (location3d_t),并创建一个通用过程 (calcdist)他们。换句话说,第二个参数有不同的类型,所以通用的想法是适用的。

在更一般的范围内,让我们在这里说主程序,为了一般性,我将我的对象声明为父类。当我分配具有子类型 (location3d_t) 的对象时,对 (calcdist) 的第二个虚拟参数为 (location3d_t) 的调用引用父泛型并表示

Error: Found no matching specific binding for the call to the GENERIC 'calcdist'

代码是

module point_mod
implicit none
type location2d_t
    integer :: x,y
contains
    procedure :: calcdist2d => calcdistance2d
    procedure :: here => here_location2d
    generic   :: calcdist => calcdist2d 
end type

type, extends(location2d_t) :: location3d_t
    integer :: z
contains
    procedure :: calcdist3d => calcdistance3d
    procedure, public :: here => here_location3d
    generic, public   :: calcdist =>   calcdist3d 
end type        

contains

function calcdistance2d(this,location) result(output)
    class(location2d_t) :: this
    type(location2d_t)  :: location
    integer :: output
    output = int(sqrt(real((location%x-this%x)**2+(location%y-this%y)**2)))
end function

function calcdistance3d(this,location) result(output)
    class(location3d_t) :: this
    type(location3d_t) :: location
    integer :: output
    output = int(sqrt(real((location%x-this%x)**2+ &
    (location%y-this%y)**2+(location%z-this%z)**2)))
end function

subroutine here_location2d(this)
    class (location2d_t) :: this
    print*, "we are in locationd2d_t"
end subroutine

subroutine here_location3d(this)
    class (location3d_t) :: this
    print*, "we are in locationd3d_t"
end subroutine
end module

模块编译没有任何错误。实现以下程序以使用该模块:

program main
use point_mod
implicit none

class (location2d_t), allocatable :: loc
type (location3d_t) :: dum

allocate(location2d_t::loc)
call loc%here() ! calls  location2d_t procedure

deallocate(loc)

allocate(location3d_t::loc)
call loc%here() !correctly calls procedure of location3d_t 

print*,loc%calcdist(dum) ! gives error

select type (loc)
type is (location3d_t)
   print*,loc%calcdist(dum) ! runs well
end select    

end program

“Here”过程正确地找到了它的动态类型。为什么不显式调用子 (calcdist) 的通用过程?即使在这种明显的情况下,我是否必须始终使用“选择类型”块? 注意:我使用 GNU fortran 4.8 和 4.9 以及 ifort 14 检查了代码。

【问题讨论】:

    标签: oop generics inheritance fortran


    【解决方案1】:

    是的,您必须使用“选择类型”。在“type is”块之外,loc 是多态的。只有在type is (location3d_t) 内部,loc 才有类型,并且可以作为具有定义类型的虚拟参数传递。

    泛型过程在类型扩展时始终不会被覆盖,因此在location3d_t 中,calcdistcalcdist3dcalcdist2d 的泛型绑定,loc 在调用calcdist 时需要特定类型找到合适的程序。

    location2d_t 扩展为location3d_t 时,here 绑定被覆盖,并且只有一个过程与loc%here() 关联,因此可以在“type is”块之外调用

    【讨论】:

    • 感谢您的回答。在 parent 的所有分支中使用“Type is”块似乎很烦人,而每个分支都可以轻松使用自己的通用过程。你知道关于泛型过程的这种行为的任何参考吗?
    • 您可以在:“Fortran 2003 手册”,第 12.5.4.1 章“通用过程名称” 中找到有关解析通用过程的一些信息
    【解决方案2】:

    您可以在不使用泛型的情况下完成此行为,只需对您的 calcdistanceXd 函数稍作调整即可。您无法覆盖扩展类型中的函数的原因是 location 的参数类型不匹配。如果您将calcdistance2d 中的location 声明为class(location2d_t),那么您可以在calcdistance3d 中匹配它。您必须在calcdistance3d 中添加select type 构造,以便从多态变量location 访问location3d_t 的成员。

    例子:

    module point_mod
      implicit none
    
      type :: location2d_t
        integer :: x, y
      contains
        procedure, public, pass(this) :: calcdist => calcdistance2d
        procedure, public, pass(this) :: here => here_location2d
      end type
    
      type, extends(location2d_t) :: location3d_t
        integer :: z
      contains
        procedure, public, pass(this) :: calcdist => calcdistance3d
        procedure, public, pass(this) :: here => here_location3d
      end type
    contains
    
      function calcdistance2d(this, location) result(output)
        class(location2d_t) :: this
        class(location2d_t)  :: location
        integer :: output
            output = int(sqrt(real((location%x-this%x)**2+(location%y-this%y)**2)))
      end function
    
      function calcdistance3d(this,location) result(output)
        class(location3d_t) :: this
        class(location2d_t) :: location
        integer :: output
        select type (location)
          type is (location3d_t)
            output = int(sqrt(real((location%x-this%x)**2+ &
            (location%y-this%y)**2+(location%z-this%z)**2)))
          class default 
            output = -1
        end select
      end function
    
      subroutine here_location2d(this)
        class (location2d_t) :: this
        print*, "we are in locationd2d_t"
      end subroutine
    
      subroutine here_location3d(this)
        class (location3d_t) :: this
        print*, "we are in locationd3d_t"
      end subroutine
    end module
    

    使用此版本的point_mod,您的示例程序可以工作:

    program main
      use point_mod
      implicit none
      class (location2d_t), allocatable :: loc
      type (location3d_t) :: dum
    
      allocate(location2d_t::loc)
      call loc%here() ! calls  location2d_t procedure
    
      deallocate(loc)
    
      allocate(location3d_t::loc)
      call loc%here() !correctly calls procedure of location3d_t 
    
      print*,loc%calcdist(dum) 
    
    end program
    

    确实,这种方法仍然需要select type,但它隐藏在模块实现中,而不是模块用户所需要的。

    【讨论】:

    • 你是对的。我必须改变我的编程风格。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2010-10-21
    • 2023-03-13
    • 1970-01-01
    • 2012-02-22
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多