В следующем модуле объявляется абстрактный тип (Base_Arrays_class
) и создаются два типа (One_Array_t
и Two_Arrays_t
). В абстрактном типе есть процедура abstract_init
, отложенная на init
.
В производных типах подпрограммы one_array_init
и two_arrays_init
используются для инициализации массива(ов).
module my_mod
implicit none
type, abstract :: Base_Arrays_class
integer :: array_size
real,dimension(:), allocatable :: array
contains
procedure(abstract_init), deferred :: init
end type Base_Arrays_class
abstract interface
subroutine abstract_init(this,array_size)
import Base_Arrays_class
class(Base_Arrays_class), intent(inout) :: this
integer, intent(in) :: array_size
end subroutine abstract_init
end interface
type, extends(Base_Arrays_class) :: One_Array_t
contains
procedure :: init => one_array_init
end type One_Array_t
type, extends(Base_Arrays_class) :: Two_Arrays_t
real,dimension(:), allocatable :: second_array
contains
procedure :: init => two_arrays_init
end type Two_Arrays_t
contains
subroutine one_array_init(this,array_size)
class(One_Array_t), intent(inout) :: this
integer, intent(in) :: array_size
this%array_size=array_size
allocate(this%array(array_size))
this%array=1.0
end subroutine one_array_init
subroutine two_arrays_init(this,array_size)
class(Two_Arrays_t), intent(inout) :: this
integer, intent(in) :: array_size
this%array_size=array_size
allocate(this%array(array_size), this%second_array(array_size))
this%array=2.0
this%second_array=3.0
end subroutine two_arrays_init
end module my_mod
Ниже приведен пример программы, использующей этот модуль.
program pgm
use my_mod
implicit none
type(One_Array_t) :: one_array
type(Two_Arrays_t) :: two_arrays
integer :: i, size
size = 4
call one_array%init(size)
call two_arrays%init(size)
print *,"one_array"
do i=1,size
print *, one_array%array(:)
end do
print *,"two_arrays"
do i=1,size
print *, two_arrays%array(:)
end do
print *
do i=1,size
print *, two_arrays%second_array(:)
end do
end program pgm
Он работает отлично.
Но теперь я хотел бы перегрузить init
, чтобы выбрать начальные значения. Например :
call one_array%init(size,4)
и call two_arrays%init(size, 5,8)
.
Итак, необходима перегрузка init
. Что-то вроде этого (для One_Array_t
):
interface init
module procedure one_array_init, one_array_init2
end interface init
Но это не соответствует abstract_init
и я не смог бы выполнить эту перегрузку.
Это возможно или нет? Спасибо за ответы.
@ Ян Буш. Спасибо за эту идею. Я не думал об этом. Я постараюсь. У вас был пример?
@ Ян Буш. Я попробовал и получил следующее сообщение об ошибке: Undefined specific binding 'one_array_init2' as target of GENERIC 'init'
Разве вы не можете пойти совершенно скучным путем one_array%init(size,[4])
и two_arrays%init(size,[5,8])
?
@francescalus. В данном конкретном случае я могу сделать это с массивом нулевого размера. Но если быть более общим, можно ли перегрузить процедуру, объявленную в абстрактном типе?
Определенная процедура, привязанная к типу, переопределяется, а не перегружается, и универсальная процедура, привязанная к типу, не может иметь то же имя, что и конкретная процедура, привязанная к типу этого типа.
@Франческаку. Так возможно или нет сделать то, что я просил?
Перегрузить init
невозможно, потому что init
— это конкретная процедура, а не универсальная процедура.
@francescalus. Спасибо за ответ. Итак, есть ли другое решение для перегрузки процедуры init
?
Действительно ли необходимо сделать процедуру init
«виртуальной» (= процедурой с привязкой к типу родительского/абстрактного типа)? stackoverflow.com/a/733393/3501546
@roygvib. В этом примере используется конструктор, но это может быть другая подпрограмма, не обязательно init
Как насчет определения процедур с привязкой к типу init1
и init2
с разными сигнатурами и присвоения им общего имени init
в родительском/базовом типе, а затем переопределения одного из init1
и init2
в дочерних типах...?
Как упоминалось в комментариях, я также думаю, что мы можем использовать generic
для этой цели, но может потребоваться использовать его в родительском типе, например...
module test_m
implicit none
type, abstract :: Parent_t
contains
generic :: sub => sub1, sub2
procedure :: sub1 => Parent_sub1
procedure :: sub2 => Parent_sub2
endtype
type, extends(Parent_t) :: Child1_t
contains
procedure :: sub1 => Child1_sub1
endtype
type, extends(Parent_t) :: Child2_t
contains
procedure :: sub2 => Child2_sub2
endtype
contains
subroutine Parent_sub1(this, n)
class(Parent_t) :: this
integer :: n
stop "sub1 not implemented"
end
subroutine Parent_sub2(this, n1, n2)
class(Parent_t) :: this
integer :: n1, n2
stop "sub2 not implemented"
end
subroutine Child1_sub1(this, n)
class(Child1_t) :: this
integer :: n
print *, "Child1: n = ", n
end
subroutine Child2_sub2(this, n1, n2)
class(Child2_t) :: this
integer :: n1, n2
print *, "Child2: n1, n2 = ", n1, n2
end
end module
program main
use test_m
implicit none
type(Child1_t) :: c1
type(Child2_t) :: c2
call c1 % sub( 100 )
call c2 % sub( 1, 2 )
!! call c1 % sub( 1, 2 ) !! stop
!! call c2 % sub( 100 ) !! stop
end
(можно протестировать на на этой странице)
Здесь я не использовал абстрактный интерфейс для родительского типа, так что в дочернем типе может быть реализована только одна интересующая процедура (например, sub1). (При необходимости в дочернем типе можно определить как sub1, так и sub2.) Новую подпрограмму, такую как sub3(), также можно добавить позже в родительский тип.
Другой подход может заключаться в том, чтобы не использовать перегрузку, а (как также упоминалось в комментариях) просто определить одну подпрограмму, которая получает более «общий» аргумент (например, массивы, производные типы и т. д.).
(Кстати, если init()
используется как «конструктор» (или «инициализатор»), я обычно не делаю его «виртуальным» в родительском типе, а напрямую определяю его в каждом дочернем типе, если конкретный тип известен инициализация и подпись init() могут различаться у разных дочерних типов. Для сравнения, этот Вопросы и ответы для C++ также может быть связан.)
Посмотрите на общий