【问题标题】:checking for self-assignment in fortran overloaded assignment在 fortran 重载赋值中检查自赋值
【发布时间】:2013-12-02 15:42:27
【问题描述】:

我正在尝试使用 fortran 2003 实现一个多项式类,该类具有重载的算术运算和赋值。派生类型维护术语定义和系数的可分配列表,如下所示

type polynomial
  private
    type(monomial),dimension(:),allocatable     ::  term
    double precision,dimension(:),allocatable   ::  coef
    integer                                     ::  nterms=0
  contains
   ...
end type polynomial

interface assignment(=)
   module procedure :: polynomial_assignment
end interface
   ...
contains
    elemental subroutine polyn_assignment(lhs,rhs)
      implicit none
      type(polynomial),intent(???)  :: lhs
      type(polynomial),intent(in)   :: rhs
 ...

我必须将其设为元素,因为它旨在用作多项式矩阵。至少在大多数情况下,这确实有效。然而,我不知何故让自己对这里的自我分配感到担忧。可以简单地检查指针以查看 C++ 中的情况是否相同,但在 Fortran 中似乎不是一种选择。但是编译器确实检测到了自赋值并给了我一个警告。 (gfortran 4.9.0)

当我有 lhs 的 intent(out) 时,lhs 和 rhs 的可分配条目似乎在进入子例程时被释放,这是有道理的,因为它们都是 p 和意图(out) 论证将首先被定稿。

然后我尝试通过意图(inout)避免释放,并通过修改 lhs 输出中的一个字段来检查自分配

   elemental subroutine polyn_assignment(lhs,rhs)
      implicit none
      type(polynomial),intent(inout)  :: lhs
      type(polynomial),intent(in)   :: rhs
      lhs%nterms=rhs%nterms-5
      if(lhs%nterms==rhs%nterms)then
        lhs%nterms=rhs%nterms+5
        return
      end if
      lhs%nterms=rhs%nterms

嗯,现在这让我感到惊讶。当我这样做时

p=p

它没有进行测试并继续进行,给了我一个 0 项但没有内存违规的多项式。迷糊了,我在assignment里面打印了lhs%nterms和rhs%nterms,结果发现它们是不一样的!

更令人困惑的是,当我对

做同样的事情时
call polyn_assignment(p,p)

它完美地工作并且检测到两个参数是相同的。我很困惑子程序的接口如何与子程序本身不同。

我错过了 Fortran 2003 中的分配有什么特别之处吗?

(第一次在这里提问。如果我做得不对,请纠正我。)

【问题讨论】:

    标签: operator-overloading fortran assignment-operator


    【解决方案1】:

    如果您有一个语句 a = b 通过子例程 sub 调用定义的赋值,则赋值语句等效于 call sub(a, (b))。注意括号 - 右侧参数是计算括号表达式的结果,因此在概念上与 b 不同。详见 F2008 12.4.3.4.3。

    因此,a = a 等同于 call sub(a, (a))。这两个参数没有别名。它与call sub(a,a) 不同,后者可能(取决于sub 的内部细节,包括虚拟参数属性)破坏Fortran 的参数别名规则(例如,在您的示例中,call polyn_subroutine(a,a) 之类的语句是非法的) .

    【讨论】:

    • 非常感谢!我很难理解标准中“括号括起来”的参考。与自分配无关,如果我们调用a=b,那么它说首先将b复制到临时c,然后调用sub(a,c)。但是,要将 b 复制到 c,程序会调用赋值吗?否则,如何为可分配的派生类型完成复制?很奇怪,这听起来像是一个迭代定义。不过,您的最后一点非常重要!我想我确实是在用非法的争论来缓和。 @IanH
    • 概念上的“创建临时副本”不被视为赋值,因此递归定义没有问题。临时的(假设编译器不够聪明,无法避免)“具有右侧的价值”,或者类似的词。如果 b 中的一个组件被分配,那么临时中的相应组件将被分配,并且分配的东西的值将被复制。
    • 你说得对,我做了更多测试,发现它通过复制每个条目生成了对象的另一个副本,并且可分配对象的指针也被复制了。所以 %nterms 字段是不同的变量,但 %term(:) 字段指针指向相同的地址,当 lhs 被释放时,rhs 也被释放。我将 lhs 更改为 intent(inout) 并仅在 lhs/rhs 的形状不符合或 lhs 未分配时才进行释放/分配,现在问题似乎已解决。谢谢!
    • 这听起来不对(或者我可能误解了你的意思)——如果a = a 调用定义的赋值,lhs 和 rhs 的可分配组件应该是完全独立的。
    • 我同意 - 我的测试也显示当前 gfortran 中的错误行为。报告为错误 59202。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 2013-10-07
    • 2018-10-11
    相关资源
    最近更新 更多