Переменные удаляются в массивах Fortran?

У меня есть следующий код с абстрактным типом, унаследованным типом и короткой программой, где я создаю объект и сохраняю его в массиве.

module m
    implicit none

    type :: container
        class(a), allocatable :: item
    end type container

    type, abstract :: a
        integer, public :: num
    end type a

    type, extends(a) :: b
        integer, public :: num2
   end type b
end module m

program mwe
    use m

    implicit none

    class(a), allocatable :: o1
    class(container), allocatable :: arr(:)

    o1 = b(1, 2)

    allocate(arr(2))
    arr(1) = container(o1)

    select type(t => o1)
        type is(b)
        write(*,*) t%num, t%num2
    end select

    select type(t => arr(1)%item)
        type is(b)
        write(*,*) t%num, t%num2
    end select
end program mwe

Проблема в том, что вывод выглядит так:

       1           2
       1           0

Как видно, у той же самой переменной, хранящейся в массиве, вторая переменная обнулена. Почему это происходит? Это потому, что массив имеет тип a, который содержит только первую переменную?

Я компилирую код с помощью ifort version 18.0.3.


person Eenoku    schedule 08.06.2018    source источник
comment
arr(1)%item = o1 works for gfortran-8.1, tio.run/, но исходный код выдает ошибку: нераспределяемая переменная не должна быть полиморфной во внутреннем назначении в (1)   -  person roygvib    schedule 08.06.2018
comment
@roygvib, в этом связанном коде вы переупорядочили определения производных типов (container после a). Это потому, что gfortran 8 еще не поддерживает порядок в вопросе?   -  person francescalus    schedule 08.06.2018
comment
Yes, gfortran gives the following error if container is above a (результат с заказом OP) (Вы можете изменить код там и нажать кнопку "Выполнить" (›) выше :)   -  person roygvib    schedule 08.06.2018
comment
Интересно, спасибо, @roygvib. Я читал, что gfortran 8 поддерживает рекурсивно размещаемые компоненты (функция Fortran 2008), поэтому меня удивило, что он не принимает первоначальный порядок.   -  person francescalus    schedule 08.06.2018


Ответы (2)


Как и в случае с ответом риперо, можно сказать, что любой вывод программы действителен. Однако мы можем внести в код простую модификацию, чтобы сделать его правильным для Фортрана.1 Этот ответ касается этой модифицированной версии.

Я бы назвал это неожиданным выводом и обратился за помощью к поставщику компилятора.

Использование конструктора структуры с полиморфными выделяемыми компонентами — одна из новых областей Фортрана. Компиляторам может потребоваться некоторое время, чтобы наверстать упущенное или сделать это правильно.

Я протестировал ваш код с помощью Intel Fortran 18.0.2 и вижу тот же результат.

На ваш вопрос

Это потому, что массив имеет тип a, который содержит только первую переменную?

Нет: в части select type с выходом t находится неполиморфная сущность типа b.

Вы можете обойти эту проблему, избегая использования конструктора структуры:

arr(1)%item = o1

Я также вижу, что компиляторы Intel до 18.0.2 все еще делают что-то другое.


1 С объявлением

    class(container), allocatable :: arr(:)

arr является полиморфным и выделяемым. Как отмечает риперо, это означает, что arr(1), элемент arr, является полиморфным. Однако, как элемент массива, arr(1) сам по себе не является полиморфным и поэтому может не находиться в левой части внутреннего оператора присваивания. Мы можем изменить код двумя способами: предоставить определенное присваивание или сделать arr не полиморфным. В коде вопроса, похоже, нет причин иметь полиморфный контейнер, поэтому я рассмотрю

type(container), allocatable :: arr(:)

Кроме того, как обсуждалось в комментариях к вопросу, если вы хотите работать с gfortran 8 или более ранней версией, чтобы увидеть, что произойдет, вам также следует изменить код в вопросе, чтобы определение производного типа container шло после определения производного типа container. производный тип a.

person francescalus    schedule 08.06.2018
comment
Тип container имеет один скалярный компонент item, тип только с одним компонентом integer. Разве не ожидается, что объект arr(1) класса container сможет хранить только один integer, а попытка напечатать второй компонент даст произвольное значение (вероятно, из-за доступа к памяти за пределами памяти)? - person jme52; 08.06.2018
comment
Компонент item является полиморфным, заявленный тип a. В нашем примере он имеет динамический тип b, который действительно имеет два компонента, потому что это был динамический тип o1 в конструкторе структуры, который был присвоен arr(1). Любая попытка напечатать компонент num2 объекта объявленного типа a должна быть обнаружена компилятором как нарушение и не компилироваться. Однако защита типа означает, что t имеет объявленный тип b. - person francescalus; 08.06.2018

Полагаю

arr(1) = container(o1)

недействителен для Fortran 2008. Это внутренний оператор присваивания, но в разделе 7.2.1.2 стандарта говорится, что

Во внутреннем операторе присваивания (1) если переменная является полиморфной, она должна быть размещаемой, а не комассивом.

Насколько я понимаю, arr(1) является полиморфным, но не выделяемым, поэтому компилятор, соответствующий стандартам, должен выдать ошибку и прервать компиляцию.

Если мои рассуждения верны, тот факт, что компилятор Intel Fortran компилирует этот код, является ошибкой компилятора, и о ней следует сообщить в Intel.

person jme52    schedule 08.06.2018
comment
Хороший вопрос, но я бы не назвал это ошибкой компилятора. Это ограничение программы, а не компилятора. [Проблема QoI, возможно, в том, что отсутствует диагностика.] Стоит отметить, что в коде, который мы видим, arr нет причин быть полиморфным (и действительно, изменение этого не устраняет неожиданный вывод). - person francescalus; 08.06.2018
comment
Насколько я понял, слово должно в стандарте устанавливает требования, которым должен удовлетворять процессор... - person jme52; 08.06.2018
comment
Теперь это основа интересного вопроса! Стандарт определяет как может выглядеть программа на Фортране, так и то, как программа на Фортране должна интерпретироваться. В целом ограничения накладываются на программы, но когда вы видите такие вещи, как C713, это означает, что компилятор должен быть в состоянии обнаруживать нарушения установленного ограничения в программе. В более широком смысле о процессоре Fortran не судят по тому, как он обрабатывает не-Fortran-программы. Подробнее см. в разделе «Обзор» стандарта. - person francescalus; 08.06.2018