Fortran 中分配和构造多态对象的规范方法是什么?

Jod*_*cus 3 oop polymorphism constructor fortran

我想创建一个多态对象数组,其中的构造函数根据其动态类型采用不同的虚拟参数。在阅读了有关用户定义和结构构造函数的内容后,我发现无法将这些概念应用于动态分配的对象。拥有 C++ 背景,我习惯了在动态或在堆栈上分配对象时可以使用同一个构造函数“成员函数”的概念,但如何在分配的对象上显式调用用户定义的 Fortran 构造函数呢?

相反,我尝试摆弄通用和类型绑定的初始化函数:

module mod
type :: basis_t
contains
    procedure, public :: init_func => init_base
    ! I want a generic constructor function
    generic, public   :: init => init_func 
end type

type, extends(basis_t) :: extended_t
contains
    ! cannot work, init_extended has a different signature from init_base
    procedure, public :: init => init_extended 
end type

type wrapper_t
   type(basis_t), pointer :: obj
end type

contains
   subroutine init_base(this)
      class(base_t), intent(inout) :: this
   end subroutine

   subroutine init_extended(this, param)
      class(extended_t), intent(inout) :: this
      integer :: param
   end subroutine
end module

program
   use mod
   implicit none

   type(wrapper_t) :: arr(2)
   allocate(basis_t::arr(1)%obj)
   allocate(extended_t::arr(2)%obj)
   call arr(1)%obj%init    ! calls init_basis
   call arr(2)%obj%init(4) ! calls init_extended
end program
Run Code Online (Sandbox Code Playgroud)

但我不相信我走在正确的轨道上,就像在 C++ 中我宁愿这样做

basis_t* arr[2];
arr[0] = new basis_t;
arr[1] = new extended_t{ 4 };
Run Code Online (Sandbox Code Playgroud)

重要的区别是 C++ 中的构造函数不是类型绑定/虚拟的,就像我的 Fortran 方法一样。我能做些什么?

Ian*_*anH 5

Fortran 中构造函数的作用可以通过以下方式提供:

  • 该语言提供了结构构造函数

  • 一个函数,其结果是正在构造的对象的类型。该语言允许泛型函数与派生类型具有相同的名称,并进一步允许对此类函数的引用来重载对该类型的结构构造函数的引用。

  • 定义适当类型的intent(out)参数的子例程。

您使用什么在一定程度上取决于环境和个人喜好。语言提供的结构体构造函数在某些情况下可以用在常量表达式中,但只允许组件的简单值定义(不允许执行代码);函数引用形式允许您作为对象构造的一部分执行任意代码,不能在常量表达式中使用,不能轻易指示构造失败,并且如果构造的对象很大,则可能会很昂贵(取决于 Fortran 处理器实现细节);子例程形式需要单独的调用语句(构造函数不能是较大表达式的一部分)并且无法利用通用名称/结构重载语言功能。

这三种方法都不涉及类型绑定过程。在某些情况下,类型绑定过程可能适合对象定义(例如,类型绑定过程旨在从文件中读取对象值 - 扩展层次结构中的所有类型都需要有关要传递到的文件的相同信息)它们),但它对于构造没有一般意义,在构造中您定义对象的类型以及定义其值。

Fortran 中的指针主要用于引用语义(因为它们是引用)。如果您想要值语义,您通常不想使用它们 - 使用可分配项。

TYPE :: ta
  INTEGER :: a
END TYPE ta

TYPE, EXTENDS(ta) :: tb
  REAL :: b
END TYPE :: tb

INTERFACE tb
  PROCEDURE :: tb_construct
END INTERFACE tb

TYPE, EXTENDS(ta) :: tc
END TYPE tc

TYPE :: ta_item
  CLASS(ta), ALLOCATABLE :: item
END TYPE ta_item

!...

FUNCTION tb_construct(arg)
  INTEGER, INTENT(IN) :: arg
  TYPE(tb) :: tb_construct
  tb_construct%a = arg + 1
  tb_construct%b = arg / 2.0
END FUNCTION tb_construct

SUBROUTINE ConstructTC(obj, arg, stat)
  CLASS(ta), INTENT(OUT), ALLOCATABLE :: obj
  INTEGER, INTENT(IN) :: arg
  INTEGER, INTENT(OUT) :: stat
  TYPE(tc), ALLOCATABLE :: tmp
  IF (arg < 0) THEN
    ! Construction failed.
    stat = 1
    RETURN
  END IF
  tmp%a = arg + 4
  CALL MOVE_ALLOC(tmp, obj)
  stat = 0    ! Construction succeeded.
END SUBROUTINE ConstructTC

!...

TYPE(ta_item) :: the_items(3)
INTEGER :: stat

! Structure constructor
the_items(1)%item = ta(1)

! Overloaded function.
the_items(2)%item = tb(2)

! Subroutine.
CALL ConstructTC(the_items(3)%item, 3, stat)
IF (stat /= 0) ERROR STOP 'It failed.'
Run Code Online (Sandbox Code Playgroud)

  • 对多态可分配项的内在赋值是 F2008 的一项功能,该功能在该版本的编译器中不可用(当前为 19.0,即将推出 20.0)。如果您使用结构体构造函数或函数来构造对象,请使用 ALLOCATE 语句的 SOURCE 说明符,即 `ALLOCATE(the_items(1)%item, SOURCE=ta(...))` (4认同)