1

我试图使用接口来调用具有不同类型的不同子例程,但是,当我使用指针属性时它似乎不起作用。例如,采取这个示例代码

 MODULE ptr_types
     TYPE, abstract :: parent
        INTEGER :: q
     END TYPE
     TYPE, extends(parent) :: child
        INTEGER :: m
     END TYPE
     INTERFACE ptr_interface
        MODULE PROCEDURE do_something
     END INTERFACE
     CONTAINS
        SUBROUTINE do_something(atype)
           CLASS(parent), POINTER :: atype
           ! code determines that this allocation is correct from input
           ALLOCATE(child::atype)
           WRITE (*,*) atype%q
        END SUBROUTINE
  END MODULE
  PROGRAM testpass
     USE ptr_types
     CLASS(child), POINTER :: ctype

     CALL ptr_interface(ctype)
  END PROGRAM

这给出了错误错误:在 (1) 处没有通用“ptr_interface”的特定子例程

但是,如果我删除子程序中的指针属性,它编译得很好。现在,通常这不会是一个问题,但对于我的用例,我需要能够将该参数视为一个指针,主要是我可以在必要时分配它。

有什么建议么?请注意,我是 fortran 的新手,所以我可能错过了一些东西

编辑:忘记将分配放在父母子程序中,初始输入未分配

编辑 2 这是我的第二次尝试,主叫方投射

    MODULE ptr_types
       TYPE, abstract :: parent
        INTEGER :: q
        END TYPE
        TYPE, extends(parent) :: child
          INTEGER :: m
        END TYPE
        TYPE, extends(parent) :: second
           INTEGER :: meow
        END TYPE
        CONTAINS
           SUBROUTINE do_something(this, type_num)
              CLASS(parent), POINTER :: this
              INTEGER type_num
              IF (type_num == 0) THEN
                 ALLOCATE (child::this)
              ELSE IF (type_num == 1) THEN
                 ALLOCATE (second::this)
              ENDIF
           END SUBROUTINE
     END MODULE
     PROGRAM testpass
        USE ptr_types
        CLASS(child), POINTER :: ctype
        SELECT TYPE(ctype)
        CLASS is (parent)
           CALL do_something(ctype, 0)
        END SELECT
        WRITE (*,*) ctype%q
     END PROGRAM

但是这仍然失败。在选择语句中,它抱怨父母必须扩展孩子。我确定这是由于处理指针属性时的限制,为了类型安全,但是,我正在寻找一种将指针转换为其父类型以进行通用分配的方法。而不是必须为每种类型编写单独的分配函数,并希望它们不会在接口或其他东西中发生冲突。

希望这个例子能更清楚地说明我想要实现的目标,如果你知道更好的方法,请告诉我

4

3 回答 3

2

如高性能标记所示,您在调用的实际参数和虚拟参数的声明类型中存在不匹配ptr_interface。如果虚拟参数具有指针或可分配属性,则不允许这样做 - 请参阅 F2008 的 12.5.2.5p2。

这种限制有一个简单的理由(在 F2008 标准的注释 12.27 中讨论) - 没有它,子例程可能会将虚拟参数分配为与实际参数不兼容的类型。例如 - 想象一下如果Parent程序中的某个地方有另一个扩展 - 类型层次结构中的兄弟Child。如果您的do_something过程将其虚拟参数分配给该同级类型,那么回到调用范围内,您将某些东西声明为Child实际上是其他不兼容(不是 Child 的扩展)类型的类型。

如果 do_something 过程不能将事物分配给 Child 类型以外的任何东西,则将其虚拟参数设置为 Child 类型。如果它可以将其分配给其他作为 Parent 扩展的类型,那么您还需要将声明的类型设为实际参数类型 Parent。您可以使用 SELECT TYPE 构造然后向下转换为调用范围内的 Child 类型的对象。

在您进行编辑之后,我的建议是让您的主程序看起来像:

PROGRAM testpass
  USE ptr_types
  IMPLICIT NONE           ! <-- 
  CLASS(Parent), POINTER :: ctype
  !***
  ! ctype here is a pointer with undefined association status, 
  ! (so no concept of dynamic type) and declared type Parent.
  CALL do_something(ctype, 0)
  ! Assuming successful ALLOCATE(Child :: xxx) in the procedure, 
  ! ctype here is an associated pointer with dynamic type Child.
  SELECT TYPE(ctype)
  CLASS is (Child)
    ! Declared type of ctype in here is Child.  Dynamic type 
    ! in this specific case is also Child, but this block would 
    ! also be executed if the dynamic type was a further extension
    ! of Child, because a CLASS IS guard was used.  (A TYPE IS 
    ! guard requires an exact match of dynamic type.)
    ! 
    ! If the allocate in do_something allocated the dummy argument 
    ! to be of type second or nullified the argument, then this 
    ! block of code would not be executed.  If do_something left 
    ! the association status of the pointer undefined, then 
    ! your program is non-conforming, and anything could happen.
    WRITE (*,*) ctype%m   
    ! Continue to work with ctype as a thing with declared type 
    ! Child inside this block of the select type construct.
  END SELECT
  ! ctype back to having a declared type of Parent.
  WRITE (*,*) ctype%q
  ! Don't forget deallocation!
 END PROGRAM
于 2013-06-26T13:38:29.490 回答
0

我认为您的问题来自子例程 do_something 中参数的 POINTER 属性。删除它,一切都应该工作。

于 2013-06-26T12:11:21.963 回答
0

如果我改变你的线路

 CLASS(child), POINTER :: ctype

 CLASS(parent), POINTER :: ctype

然后你的程序编译并执行。我自己对所有这些面向对象的 Fortran 都很陌生,所以我很难指出标准中的条款,该条款说明了这种情况下等级类型匹配的规则并澄清了你的错误。您的错误可能只是使用了没有实现该语言最新功能的编译器。另一方面,也许我的编译器(英特尔 Fortran 13.1)与您的编译器一样错误地实现了最新功能。

(在过去的表格中,SO 上一个名叫 IanH 的人稍后会经过并澄清。)

不过,我学到的一件事是,如果您的编译器符合 Fortran 2003(足够),那么制作变量ALLOCATABLE而不是POINTER使许多操作更容易,并将释放不需要的内存的责任传递给编译器。您不再需要在 Fortran 中进行动态内存管理的指针。

于 2013-06-26T09:29:30.577 回答