Fortran - 通用接口和特定接口之间的区别

boo*_*toT 6 fortran interface

我试图理解抽象接口和"普通"接口之间的区别.什么使界面抽象?什么时候需要?

假设下面的例子

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
Run Code Online (Sandbox Code Playgroud)

输出是

5.000000
2.000000
2.000000
Run Code Online (Sandbox Code Playgroud)

我这里没有使用抽象接口.至少我觉得我没有?我已经这样做了一段时间,我从来没有在接口上使用抽象的"限定符".似乎我没有找到需要使用抽象接口的情况.

有人可以在这里启发我吗?

PS:编译器英特尔Visual Fortran Composer XE 2013 SP1更新3.


编辑:

引用现代Fortran中的Metcalf,Reid和Cohen解释:

在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
Run Code Online (Sandbox Code Playgroud)

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

Vla*_*r F 6

"普通"接口 - 标准称为特定接口块(正如您在问题标题中使用的那样) - 只是某些过程的常规接口块.因此:

interface
  subroutine foo_sub
  end subroutine
end interface
Run Code Online (Sandbox Code Playgroud)

表示存在一个名为的实际(外部)子例程foo_sub,它符合指定的接口.

一个抽象的界面

abstract interface
  subroutine foo_sub_abs
  end subroutine
end interface
Run Code Online (Sandbox Code Playgroud)

只是指定一些过程的外观,但名称是接口的名称,而不是任何实际过程.它可以用于过程指针

procedure(foo_sub_abs), pointer :: p
Run Code Online (Sandbox Code Playgroud)

或者是虚假的论点

subroutine bar(f)
  procedure(foo_sub_abs) :: f
Run Code Online (Sandbox Code Playgroud)

并且它意味着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
Run Code Online (Sandbox Code Playgroud)

因为没有名为的实际程序abstract_foo.有些编译器没有对此进行诊断,但他们可以.


非常不相关的是通用接口.您可以识别它们,因为在该单词后面有一个通用程序的名称interface

  interface generic_sub
    procedure sub1

    subroutine sub2(...)
    end subroutine
  end interface
Run Code Online (Sandbox Code Playgroud)

这里sub1sub2两者都存在,sub1已经知道并且已经有明确的接口可用,sub2是外部的并且看起来像接口所指定的,并且两者都是泛型的特定过程generic_sub.这是一个完全不同的用法.

然后你打电话

call generic_sub(...)
Run Code Online (Sandbox Code Playgroud)

并根据您传递的参数,编译器选择调用哪个特定过程,如果是sub1,或sub2.