Конфликт между назначением копии и конструктором в Fortran

#fortran #intel-fortran

#fortran #intel-fortran

Вопрос:

У меня есть следующий код, который вызывает ошибку сегментации. Он жалуется, что

forrtl: серьезный (408): fort: (7): Попытка использовать указатель TT, когда он не связан с целью

Теперь я почти уверен, в чем причина, а именно, он пытается получить доступ к моей copy процедуре назначения, в то время как я просто пытаюсь инициализировать объект.

Путем закомментирования generic :: assignment(=) => copy это работает отлично!

Я компилирую код следующим образом: (IFORT версии 19.0.3) ifort -O0 -debug full -check all -traceback -g -C -CB -CU -CA -fpp filaname.f90 и запускаю с помощью ./a.out

    module md

   implicit none

   type T_TEST

      integer :: ii

   contains
         procedure, pass(this)               :: COPY
       generic :: assignment(=) => copy

   end type


   interface t_test
      module procedure init
   end interface t_test


   type(t_test) , allocatable :: tt


contains
   function init( size )
      integer, intent(in)                   :: size
      type(t_test) , allocatable           :: init


      allocate( init )
      init% ii = size
   end function

   subroutine copy(this, old )

      class(t_test), intent(out)  ::this
      type(t_test), intent(in) :: old


       this% ii = old% ii

   end subroutine

end module md

program t_Testprogram
use md
implicit none

tt = t_test( 100 )

end program t_Testprogram
  

Комментарии:

1. В вашем коде нет указателя, просто выделяемый. Вы на 100% уверены, что показываете точный код, который выдает ошибку?

2. Да, безусловно. Я только что проверил еще раз. Если вы компилируете с показанными флагами, я бы хотел, чтобы вы получили то же сообщение об ошибке, что и при использовании ifort

3. Я только что попробовал gfortran, и он просто выдает segmentation fault - invalid memory reference . Опять же, это говорит о том, что вызов tt = t_test( 100 ) каким-то образом заканчивается в процедуре назначения копирования. Потому что, когда я комментирую общее назначение, оно работает отлично.#

4. Ну, конечно, это относится к copy общему назначению. Вот почему у вас это есть, чтобы вызываться, когда = есть в коде, не так ли?

Ответ №1:

Причина в том, что перегруженное назначение copy не поддерживает выделяемые левые части. Таким образом, когда значение this используется в this% ii = old% ii , оно фактически не существует и используется нулевой указатель. Но я согласен, что сообщение об ошибке Intel сбивает с толку или даже неверно.

Автоматическое выделение левой части (повторное) применяется только к интризичным назначениям, а не к определяемым пользователем. В пользовательских вы должны сами запрограммировать точное поведение. И вы ничего не указали для не выделенных левых сторон.

Это работает для меня:

    type T_TEST

      integer :: ii

   end type


   interface assignment(=)
     procedure copy
   end interface


   subroutine copy(this, old )

      class(t_test), allocatable, intent(out)  ::this
      type(t_test), intent(in) :: old

       if (.not.allocated(this)) allocate(this)
       this% ii = old% ii

   end subroutine
  

Или вы можете просто сначала выделить объект (это то, что я бы сделал здесь, потому что gfortran, похоже, не нравится общее разрешение на основе атрибута allocatable — функции F08).

 allocate(tt)
tt = t_test( 100 )
  

Похоже, вы думаете, что только потому, что у конструктора есть переменная результата «marked» allocatable , он выделит левую часть назначения для вас. Это не так. Единственное, что он делает, это то, что он выделяет свой собственный результат в качестве временной переменной. Затем этот результат присваивается в tt = t_test() , а затем автоматически освобождается.

Помните, что результирующая переменная не совпадает с левой частью назначения. Результат может быть использован в выражении многих различных типов, а не только в присваивании. Это может быть передано подпрограмме, это может быть использовано в арифметическом выражении, это может быть напечатано…

Ваш конструктор может быть просто

    function init( size )
      integer, intent(in)                   :: size
      type(t_test)           :: init

      init% ii = size
   end function
  

и результат будет точно таким же. Нет причин делать его распределяемым, это просто усложняет его, но ни в малейшей степени не меняет результат.

Возможно, вы пытаетесь следовать некоторым принципам C RAII, но помните, что C — это просто не Fortran.

Комментарии:

1. Чего я хотел, так это: если я скажу tt = t_test (100), он должен действовать как конструктор. Таким образом, у меня есть интерфейс процедуры модуля для инициализации. Хотя позже, если я сделаю tt1 = tt2 , это должна быть процедура назначения копии. Да, идея заключается в том, что когда LHS является распределяемым, он должен быть конструктором, а не в том случае, когда он выделяется. Можете ли вы объяснить, почему компилятор решает обратиться к процедуре копирования, когда я выполняю tt = t_test (100). Я уже определил, что t_test должно ссылаться на INIT подпрограмму

2. @A2LBK t_test(100) действительно создает значение, но затем оно должно быть присвоено tt . Вот почему должно быть вызвано пользовательское назначение.

3. @A2LBK Вы, кажется, используете терминологию C , это бесполезно, Fortran сильно отличается от C .

4. «идея заключается в том, что, когда LHS является распределяемым, он должен быть конструктором, а не в том случае, когда он выделяется». Стандарт Fortran не поддерживает никаких подобных идей, это просто ваше желание.

5. Ах, теперь я понимаю, почему это попадает в копию. Спасибо! Обычно я использовал конструкторы такого типа, поскольку это было то, что многие советовали при попытке создать конструкторы в Fortran. Но я понимаю, почему это может легко привести к конфликту. Таким образом, в принципе, более или менее невозможно одновременно иметь конструктор и копировать назначение в одном объекте