"Operation is invalid" 使用 ifort 为派生类型重载加法时

"Operation is invalid" when overloading addition for a derived type using ifort

这是我之前开始的一个话题的后续问题

基本上,我想要实现的是定义一个自动分配原始类型的延迟类型(实数、整数、字符和逻辑)。您可以在下面看到一个工作示例上面的 link 并用 gcc version 7.3.0ifort version 18.0.0.

编译

我现在已经扩展了代码,以便 "use" 延迟数据类型,但不知道分配的原始类型是什么。这是通过覆盖基本运算符来实现的。为了简单起见,我在以下示例中仅包含 + 运算符。该示例可以使用 gfortran 进行编译,但是在使用 ifort 进行编译时会出现错误:

error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. [PLUS_FUNC_CLASS] c%i = a%i + b%i

有谁知道这里的问题是什么?我已经用谷歌搜索了这个错误,但我找不到我做错了什么。


注意

为了匹配精度,我使用了以下 编译标志

ifort -r8 -i8 tst.f90

gfortran -fdefault-real-8 -fdefault-integer-8 -fdefault-double-8 tst.f90


示例代码如下:

module DervType

  implicit none

  type, public :: mytype
    real                          :: r
    integer                       :: i
    character(len=:), allocatable :: c
    logical                       :: l
  end type

  interface assignment(=)
    module procedure equal_func_class
  end interface

  interface operator(+)
    module procedure plus_func_class
  end interface

contains

   subroutine equal_func_class(a,b)
        type(mytype), intent(out):: a
        class(*),     intent(in) :: b

        select type (b)
            type is (mytype)
                print *, "is mytype"
                if ( .not. a%r == b%r ) a%r = b%r      !! <-- ugly, but necessary not to end up in an endless loop when reassigning mytype (only testing and assigning real here)
            type is (real)
                print *, "is real"
                a%r = b
            type is (integer)
                print *, "is int"
                a%i = b
            type is (character(len=*))
                print *, "is char"
                a%c = b
            type is (logical)
                print *, "is logical"
                a%l = b 
        end select

    return

  end subroutine equal_func_class


  recursive function plus_func_class(a,b) result(c)
        class(*), intent(in)        :: a
        class(*), intent(in)        :: b
        type(mytype)                :: c

        select type (a)
            type is (mytype)
                print *, "left side is mytype"

                !! -------------------------------
                !! only testing one case here and only real operations are
                !! taken care of!
                !! -------------------------------
                select type (b)
                    type is (mytype)
                        print *, "right side is mytype"                       
                        c%i = a%i + b%i  !! <-- this is where ifort throws the error
                        c%r = a%r + b%r  !! <-- this is where ifort throws the error 
                    type is (real)
                        print *, "right side is real", a%r
                        c = a%r + b
                end select

            !! do similar logic when the operands changing sides
            type is (real)
                print *, "left side is real"
        end select

        !c = 1.

        return        

    end function plus_func_class


end module DervType

program TestType

  use DervType

  implicit none

  type(mytype) :: test, test2, res, res2

  real, parameter :: tt = 2.

  test = 1.
  test = 1
  test = "Hey Tapir"
  test = .true.

  test2 = 2.

  test = test2

  print *, "test = ", test%r

  res  = test + 1.0
  res2 = test + tt

  print *, "Calculation 1 (real) : ", res%r
  print *, "Calculation 2 (real) : ", res2%r

end program TestType

当使用 gfortran 和 运行 编译时,程序会给出以下输出:

 is real
 is int
 is char
 is logical
 is real
 is mytype
 test =    2.0000000000000000     
 left side is mytype
 right side is real   2.0000000000000000     
 is real
 is mytype
 left side is mytype
 right side is real   2.0000000000000000     
 is real
 is mytype
 Calculation 1 (real) :    3.0000000000000000     
 Calculation 2 (real) :    4.0000000000000000 

让我们将这个示例程序简化为更易于管理的内容:

 module DervType

  implicit none

  type mytype
    integer i
  end type mytype

  interface operator(+)
    module procedure plus_func_class
  end interface

contains

  recursive function plus_func_class(a,b) result(c)
    class(*), intent(in)        :: a
    class(*), intent(in)        :: b
    type(mytype)                :: c

    c%i = 1+1
  end function plus_func_class

end module DervType

我的 ifort 18.0.3 抱怨这个:

error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands.   [PLUS_FUNC_CLASS]
    c%i = 1+1
-----------^

熟悉吗?

嗯,看起来是因为我们有 plus_func_class 的参数,因为无限多态 ifort 决定将此函数作为泛型 operator(+) 的特定过程,表达式 1+1. (删除 recursive 前缀以进一步查看。)

我们不希望它这样做。我们能说服它不要吗?

我们希望我们的真实函数考虑左侧或右侧为 class(mytype) 的情况,因为我们不关心重新实现 intrinsic+intrinsic。我不会写出细节,但你可以实现该功能两次:一次使用 LHS class(mytype) 和 RHS class(*),一次使用 LHS class(*) 和 RHS class(mytype) .


即使最初将此方法简单地视为 "compiler bug workaround",在不为加法运算的两边使用无限多态参数的情况下实现定义的运算确实是值得的

当您想创建一个新类型时 mytype2 您不想用函数 plus_func_class 定义操作。不过你需要这样做,因为如果你为泛型 operator(+).

创建一个新的特定函数,你将有一个不明确的接口