Присвоение размещаемого массива самим значениям

Я хотел бы знать, возможно ли в современном Фортране назначить выделяемый массив, используя себя или его часть для этого. Вот простой пример:

module modu
implicit none

type :: t
  integer :: i
end type

contains

subroutine assign(a,b)
type(t), allocatable, intent(out) :: a(:) 
type(t),              intent(in)  :: b
allocate(a(1))
a(1) = b
end subroutine
end module

!----------------------

program test
use modu
implicit none
type(t), allocatable :: a(:)

allocate(a(1))
a(1)%i = 2
call assign(a, a(1))
print*, a(1)%i
end program

Этот код дает ответ corect с ifort 18 и возвращает "Segmentation fault" с gfortran 7.4.

ПРИМЕЧАНИЕ. Исходная проблема была немного более сложной, так как call assign(a, a(1)) следует заменить на call assign(a, a(1)+b) с должной перегрузкой operator +, но вывод (с уважением к ifort и gfortran) тот же.

ПРИМЕЧАНИЕ. В потоке проверка самоназначения в перегруженном назначении fortran, @IanH делает различие между call assign(a,a) и call assign(a,(a)), но я считаю, что это не решает эту проблему, потому что у меня есть аргументы, которые можно распределять.

ПРИМЕЧАНИЕ. В потоке Автоматическое выделение массива при назначении в Fortran, @ francescalus объясняет автоматическое распределение по внутреннему присваиванию, но я снова считаю, что здесь это не применимо.


person franpena    schedule 22.10.2019    source источник


Ответы (2)


Можно назначить выделяемый массив, используя элементы массива того же самого массива. Например, для a размещаемых

a = [a(2), a(1)]

действительно и имеет ожидаемый результат. То же самое независимо от того, является ли a внутренним типом (для внутреннего присваивания) или производным типом (для внутреннего или определенного присваивания). Правая часть рассматривается как полностью вычисленная как выражение до того, как будет затронута левая часть.

Однако использование call assign(a,a(1)) - это не то же самое, что это присвоение.

Здесь уместен момент в ответе IanH, на который вы ссылаетесь. Определенное присвоение будет похоже на call assign(a,(a(1))), а не на call assign(a,a(1)).

Это важно из-за ограничений псевдонима, упомянутых в ответе Владимира Ф., который я не буду повторять. Использование call assign(a,(a(1))) удаляет псевдонимы, потому что (a(1)) - это выражение со значением, равным значению элемента массива, а не самого элемента массива. Это похоже на атрибут value, который упоминает Владимир Ф, но без создания определяемой сущности.

Определенное присвоение использует конструкцию (rhs) именно для того, чтобы избежать этой проблемы сглаживания.

Что касается ваших результатов gfortran, IanH в ответ создал отчет об ошибке для GCC. на этот другой вопрос. Это может быть уместно при объяснении вашего удивления.

person francescalus    schedule 22.10.2019

Этот звонок

call assign(a, a(1))

запрещено правилами псевдонима Фортрана. Вы передаете одно и то же значение в разные аргументы, и ни одно из них не является pointer или target, и вы изменяете один из них.

Затем компилятор освобождает a, потому что это intent(out). Это означает, что старого a(1) больше не существует. Но b по-прежнему указывает на это. Затем вы размещаете новый a в другом месте. Затем вы пытаетесь использовать b в

a(1) = b

и это обречено на неудачу, потому что указывает на какой-то неопределенный фрагмент памяти. Вам просто повезло с Intel Fortran, но ваш код незаконен. Возможно, Intell разместила новый a на том же месте, где находился старый a, но это чистая удача.

Это сработает, если вы сделаете

type(t), allocatable, intent(inout) :: a(:) 
type(t),              value  :: b
deallocate(a)
allocate(a(1))
a(1) = b

Я не уверен, почему он вылетает с intent(out) и value. Это может быть проблема компилятора, о которой сообщается как ошибка 92178.

person Vladimir F    schedule 22.10.2019
comment
Спасибо, ваш ответ очень полезен. Я выбрал @francescalus как решение вопроса, потому что он также указал на связь с определенным назначением, которую я не мог обнаружить. - person franpena; 22.10.2019
comment
Возможно, это не проблема компилятора. Код может быть несоответствующим. - person steve; 22.10.2019
comment
@steve Возможно, именно поэтому я написал май. См. Обсуждение в отчете об ошибке. Все не так просто. Моя интерпретация моего кода такова, что он соответствует. См. Также ответ Франческала. - person Vladimir F; 22.10.2019
comment
Я веду обсуждение в отчете об ошибке. :-) - person steve; 22.10.2019
comment
Оппс, это ты! Я никогда не понимал. :) Я думал, у тебя был другой логин несколько лет назад, а потом исчез. - person Vladimir F; 22.10.2019
comment
Все сводится к тому, что в стандартном языке означает вызов процедуры? 15.5.4 обеспечивает последовательность: оцениваются фактические аргументы, затем ассоциация аргументов, поэтому требования в 15.5.2.13 и p. 308 должен появиться только после объединения аргументов. К сожалению, изучение языка стандарта в Разделе 15 вызывает у меня головную боль. - person steve; 22.10.2019