Fortran中“抽象”接口和“普通”接口的区别

12

我正试图理解抽象接口和“普通”接口之间的区别。什么使一个接口成为抽象接口?何时需要使用每种类型的接口?

假设以下是示例:

module abstract_type_mod
  implicit none

  type, abstract :: abstract_t
  contains
    procedure(abstract_foo), pass, deferred :: Foo
  end type

  interface
    subroutine abstract_foo ( this, a, b )
      import :: abstract_t
      implicit none
      class(abstract_t), intent(in)  :: this
      real,              intent(in)  :: a
      real,              intent(out) :: b
    end subroutine
  end interface

end module

module concrete_type_mod
  use abstract_type_mod
  implicit none

  type, extends ( abstract_t ) :: concrete_t
  contains
    procedure, pass :: Foo
  end type

  contains

  subroutine Foo ( this, a, b )
    implicit none
    class(concrete_t), intent(in)  :: this
    real,              intent(in)  :: a
    real,              intent(out) :: b

    b = 2 * a

  end subroutine
end module 

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module subs_mod
  implicit none

  contains

  pure subroutine module_foo ( a, b )
    implicit none
    real, intent(in)  :: a
    real, intent(out) :: b

    b = 2 * a

  end subroutine

end module

program test
  use ifaces_mod
  use subs_mod
  use concrete_type_mod
  implicit none

  type(concrete_t) :: concrete
  procedure(foo_sub) :: external_sub
  procedure(foo_sub), pointer :: foo_ptr
  real :: b

  foo_ptr => external_sub

  call foo_ptr ( 0.0, b )
  print*, b

  foo_ptr => module_foo

  call foo_ptr ( 1.0, b )
  print*, b

  call concrete%Foo ( 1.0, b )
  print*, b

end program

pure subroutine external_sub ( a, b )
  implicit none
  real, intent(in)  :: a
  real, intent(out) :: b

  b = a + 5

end subroutine

输出结果为

5.000000
2.000000
2.000000

我在这里没有使用抽象接口。至少我认为我没有? 我已经这样做了一段时间,并且从未在接口上使用过“抽象”限定符。似乎我还没有找到需要使用抽象接口的情况。

有人能给我启示吗?

PS:编译器Intel Visual Fortran Composer XE 2013 SP1 Update 3。


编辑:

引用Metcalf,Reid和Cohen在《现代Fortran解释》中的话:

在Fortran 95中,要声明具有显式接口的虚拟或外部过程,需要使用接口块。这对于单个过程来说很好,但对于声明具有相同接口(除了过程名称)的多个过程来说有点冗长。此外,在Fortran 2003中,有几种情况会变得不可能(过程指针组件或抽象类型绑定过程)。

那么,我的编译器是否因接受以下代码以及上面带有抽象类型的代码而出错?

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module my_type_mod
  use ifaces_mod
  implicit none

  type my_type_t
    procedure(foo_sub), nopass, pointer :: Foo => null()  
  end type

end module

在这两种情况下,我会说我实际上已经声明了抽象接口,而没有使用抽象关键字。我认为我的困惑根源于编译器接受这样的代码。

1个回答

12

“正常”的接口,标准称之为特定接口块(如您在问题标题中使用的那样),只是某个过程的普通接口块。因此:

interface
  subroutine foo_sub
  end subroutine
end interface
意味着存在一个名为foo_sub的实际(外部)子例程,并且它符合指定的接口要求。
抽象接口。
abstract interface
  subroutine foo_sub_abs
  end subroutine
end interface

接口只是指定了某个过程的外观,但名称是接口的名称,而不是任何实际过程的名称。它可用于过程指针。

procedure(foo_sub_abs), pointer :: p

或用于虚拟参数

subroutine bar(f)
  procedure(foo_sub_abs) :: f

这意味着p所指向的实际过程,或传递给f的过程符合抽象接口。

请注意,在前两个示例中,您可以使用现有过程代替抽象接口。它只需要在作用域中具有显式接口(通常在同一模块中或在使用的模块中)。


据我所知(但请参见@IanH下面的评论),编译器有权拒绝您的代码:

  interface
    subroutine abstract_foo ( this, a, b )
      import :: abstract_t
      implicit none
      class(abstract_t), intent(in)  :: this
      real,              intent(in)  :: a
      real,              intent(out) :: b
    end subroutine
  end interface

因为不存在名为abstract_foo的实际过程。一些编译器没有诊断这个问题,但它们可以。


与此毫不相关的是通用接口。您可以通过单词interface后面的通用过程名称来识别它们。

  interface generic_sub
    procedure sub1

    subroutine sub2(...)
    end subroutine
  end interface

这里 sub1sub2 都存在,其中 sub1 已经知道并且已经有了明确的接口可用,sub2 是外部的并且看起来与接口规定一样,并且两者都是泛型 generic_sub 的特定过程。这是一种非常不同的用法。

然后你调用

call generic_sub(...)

根据您传递的参数,编译器选择调用哪个具体的过程,如果是 sub1sub2

我想强调的是,这些可以分成不同的接口块,并在不同的位置声明相同名称。您可以通过这种方式将特定过程添加到现有的通用过程中。


谢谢,弗拉德米尔。让我更困扰的例子实际上是抽象类型的例子。从概念上讲,我会说我在那里声明的接口是抽象的,但我从来没有不得不这样声明它们。这使我得出结论,我真的不知道它们是什么。现在,关于过程指针和过程虚拟参数。我也不为这些声明接口为抽象的。你能给我一个例子吗?如果我不声明一个抽象接口,它将根本无法工作?(除了你已经说编译器可能会拒绝的那个例子之外。) - booNlatoT
你并不一定需要抽象接口。你总是可以创建一个虚拟过程,并在 procedure() 中指向它。因此,抽象接口主要是为了方便和语言的完整性。 - Vladimir F Героям слава
是的,通过虚拟程序我指的是一个什么有用的事情都不做但有正确接口的存根。 - Vladimir F Героям слава
1
关于您的编辑,没有任何更改。您仍然存在错误,因为不存在 foo_sub,且接口未声明为抽象。请注意,编译器不需要诊断此类问题,只允许其这样做。要更正您的代码,只需在单词“interface”之前添加单词“abstract”。 - Vladimir F Героям слава
在某些情况下允许非抽象接口体没有相应的过程定义(例如在问题中)-请参阅F2008 12.4.3.2p7。 - IanH
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接