我有一个派生类型,它包含一个可设置的可分配数组,我试图重载一些运算符。由于我的数组变得非常大,我不希望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
的位置。
我的问题:
+
运算符时避免复制,或者我应该放弃这个想法?答案 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表示。