Fortran在可分配数组上的接口操作符行为

时间:2015-02-26 13:53:40

标签: fortran operator-overloading fortran90 dynamic-arrays

我有一个派生类型,它包含一个可设置的可分配数组,我试图重载一些运算符。由于我的数组变得非常大,我不希望Fortran对我的数组进行隐式复制,但我无法弄清楚它实际上是做什么的。这是一个说明我的观点的最小程序:

module type_vector_field
  implicit none


  type vector_field
     double precision, dimension(:), allocatable :: f
  end type vector_field

  interface operator(+)
     module procedure vector_field_add
  end interface


contains


  function vector_field_add(vf1, vf2) result(vf3)
    type(vector_field), intent(in) :: vf1, vf2
    type(vector_field) :: vf3

    print*, "vf* addresses in add:  ", loc(vf1), loc(vf2), loc(vf3)
    vf3%f = vf1%f + vf2%f

  end function vector_field_add


  subroutine vector_field_add2(vf1, vf2, vf3)
    type(vector_field), intent(in) :: vf1, vf2
    type(vector_field), intent(out) :: vf3

    print*, "vf* addresses in add2: ", loc(vf1), loc(vf2), loc(vf3)
    vf3%f = vf1%f + vf2%f

  end subroutine vector_field_add2


end module type_vector_field

可以通过vector_field运算符或+子例程添加两个vector_field_add2。我使用以下示例程序对此进行了测试:

program main
  use type_vector_field
  implicit none

  integer :: n = 6283
  type(vector_field) :: vf1, vf2, vf3

  allocate(vf1%f(n), vf2%f(n))
  vf1%f = 3.1415
  vf2%f = 3.1415

  !! first version

  allocate(vf3%f(n))
  print*, "vf* addresses in main: ", loc(vf1), loc(vf2), loc(vf3)

  vf3 = vf1 + vf2
  print*, "vf* addresses in main: ", loc(vf1), loc(vf2), loc(vf3)
  print*, vf3%f(n)

  deallocate(vf3%f)

  !! second version

  allocate(vf3%f(n))
  print*, "vf* addresses in main: ", loc(vf1), loc(vf2), loc(vf3)

  call vector_field_add2(vf1, vf2, vf3)
  print*, "vf* addresses in main: ", loc(vf1), loc(vf2), loc(vf3)
  print*, vf3%f(n)

end program main

这是用gfortran-4.8.2编译时输出的内容:

$ gfortran -g -Wall test.f90 -o test && test
 vf* addresses in main:           6303968          6304032          6304096
 vf* addresses in add:            6303968          6304032  140733663003232
 vf* addresses in main:           6303968          6304032          6304096
   6.2829999923706055     
 vf* addresses in main:           6303968          6304032          6304096
 vf* addresses in add2:           6303968          6304032          6304096
 vf* addresses in main:           6303968          6304032          6304096
   6.2829999923706055 

子程序似乎直接与主作用域中定义的vf3实例一起工作,而运算符在其作用域中创建另一个vf3实例并复制结果。当我打印数组地址而不是派生类型地址时,我得到:

 vf*%f addresses in main:          39440240         39490512         39540784
 vf*%f addresses in add:           39440240         39490512                0
 vf*%f addresses in main:          39440240         39490512         39591056
   6.2829999923706055     
 vf*%f addresses in main:          39440240         39490512         39540784
 vf*%f addresses in add2:          39440240         39490512                0
 vf*%f addresses in main:          39440240         39490512         39540784
   6.2829999923706055     

因此,运算符似乎在主范围的vf3%f实例中复制其vf3%f的地址,而子例程直接与主范围的vf3%f一起工作。在这两种情况下,我都不明白子程序和运算符的vf3%f的位置。

我的问题:

  • 有没有办法确定运算符和子程序在哪个数组中写出结果?
  • 有没有办法在使用+运算符时避免复制,或者我应该放弃这个想法?

1 个答案:

答案 0 :(得分:1)

据我所知,您遇到的问题与运算符重载无关。

或多或少偶然(我推测)你的子程序vector_field_add2中有INTENT(OUT),而你在vector_field_add中没有它。

INTENT(OUT)的一个经常被忽视的影响是,如果伪参数在进入vector_field_add2时具有allocatable属性,则所有可分配数组都会被释放。具有可分配属性的组件的类型也是如此。

如果你有

   real, dimension(:), allocatable: my_array

   allocate(my_array(some:thing))

然后再做

 call sub1(my_array)

  sub1(a)
    real, dimension(:), intent(out) :: a

没有什么令人惊讶的事情发生。

如果你改为

   call sub2(a)

其中sub2是

   subroutine sub2(a)
   real, dimension(:), allocatable, intent(out) :: a
   end subroutine sub2

对sub2的调用相当于对deallocate的调用。如果您遗漏intent(out)或替换它,例如intent(inout),没有任何反应。

同样的事情发生在你身上,只有你的类型中有可分配的组件,所以它更不可见。

否则,如果您使用运算符或直接调用子例程,则没有区别。它只对清晰的代码很重要。编译器将赋值语句与重载操作符一起转换为子例程调用。

如果您想了解它是如何完成的,请使用-fdump-tree-original选项翻译您的程序。这将生成一个主要名称的文件,如源文件,添加.003.original,您可以在其中看到Fortran代码的类C表示。