Nested derived type with overloaded assignment

2019-04-07 08:30发布

问题:

I have a derived type (wrapper) containing an other derived type (over). For the latter the assignment operator have been overloaded. As the assignment of derived types happens per default componentwise, I'd expect that assigning two instances of wrapper would invoke the overloaded assignment for over at some point. However, using the program below, it does not seem to be the case. The overloaded assignment is only invoked if I also overload the assignment for wrapper containing an explicit assignment between instances of over (by uncommenting the commented code lines). Why? I find it somewhat counter intuitive. Is there any way to avoid the overloading in the wrapping type?

module test_module
  implicit none

  type :: over
    integer :: ii = 0
  end type over

  type :: wrapper
    type(over) :: myover
  end type wrapper

  interface assignment(=)
    module procedure over_assign
    !module procedure wrapper_assign
  end interface assignment(=)

contains

  subroutine over_assign(other, self)
    type(over), intent(out) :: other
    type(over), intent(in) :: self

    print *, "Assignment of over called"
    other%ii = -1

  end subroutine over_assign

  !subroutine wrapper_assign(other, self)
  !  type(wrapper), intent(out) :: other
  !  type(wrapper), intent(in) :: self
  !
  !  other%myover = self%myover
  !
  !end subroutine wrapper_assign

end module test_module

program test
  use test_module
  implicit none

  type(wrapper) :: w1, w2

  print *, "Assigning wrapper instances:"
  w2 = w1

end program test

回答1:

This [unfortunate] situation is a consequence of the rules of the language (F90+) for intrinsic assignment of derived types. The details are spelled out in F2008 7.2.1p13. As a summary, intrinsic assignment of derived types (the assignment that happens with the wrapper_assign specific commented out) does not invoke non-type bound defined assignment for any components that are of derived type. In F90/F95, if you want defined assignment at some lower level of the component hierarchy then you need to have defined assignment for all the parent components up to the base object.

F2003 added type bound defined assignment to the language and this is invoked by intrinsic assignment of derived types. Use that instead of the stand-alone generic form of specifying defined assignment. (This also avoids a potential problem with the type name being accessible but the defined assignment procedure not being accessible.)



回答2:

Just to complete the thread: the concrete realisation of IanH's suggestion (please upvote his original answer rather than this one) which worked for me was the following one:

module test_module
  implicit none

  type :: over
    integer :: ii = 0
  contains
    procedure :: over_assign
    generic :: assignment(=) => over_assign
  end type over

  type :: wrapper
    type(over) :: myover
  end type wrapper

contains

  subroutine over_assign(other, self)
    class(over), intent(out) :: other
    class(over), intent(in) :: self

    print *, "Assignment of over called"
    other%ii = -1

  end subroutine over_assign

end module test_module


program test
  use test_module
  implicit none

  type(wrapper) :: w1, w2

  print *, "Assigning wrapper instances:"
  w2 = w1

end program test