Перегрузка отложенной процедуры неполиморфной процедурой в Fortran 2008

Можно ли перегрузить отложенную процедуру неполиморфной процедурой?

Я хотел бы создать абстрактный класс (Parent) с процедурой (foo), которая должна быть перегружена каждым классом, расширяющим Parent. Я сталкиваюсь с проблемами, когда хочу снова расширить, например, класс (Grandchild) расширяет класс (Child), который расширяет Parent.

Поскольку Child не является абстрактным, его foo (foo_Child) должен быть полиморфным. Но тогда Grandchild наследует foo_Child, а не определяет foo_Grandchild. Кроме того, поскольку я не хочу, чтобы foo_Child был полиморфным, я хочу иметь возможность использовать неполиморфные функции, специфичные для Child, внутри foo_Child.

module test_module

  type, abstract :: Parent
  contains
    procedure(foo_Parent), deferred :: foo
  end type

  abstract interface
    subroutine foo_Parent(this,input)
      import Parent
      class(Parent), intent(out) :: this
      character(*),  intent(in)  :: input
    end subroutine
  end interface

  type, extends(Parent) :: Child
  contains
    procedure :: foo => foo_Child
  end type

  type, extends(Child) :: Grandchild
    ! Is not required to define foo=>foo_Grandchild.
    ! This is not the behaviour I want.
  end type

  interface Child
    module procedure new_Child
  end interface
contains

function new_Child(input) result(this)
  character(*), intent(in) :: input
  type(Child)              :: this
end function

subroutine foo_Child(this,input)
  type(Child),  intent(out) :: this ! Fails: 'this' is not polymorphic.
  character(*), intent(in)  :: input

  this = Child(input) ! Fails if type(Child) is replaced by class(Child).
end subroutine
end module

program test
  use test_module
end program

Чтобы обобщить:

Есть ли способ сделать foo_Child неполиморфным, но при этом перегрузить foo_Parent? Или есть способ вызова неполиморфных функций (по крайней мере, Child=Child присваивания с неполиморфной правой стороной) в полиморфной процедуре? Если нет, есть ли обходной путь?

(Я не хочу определять class(Child)=type(Child), но буду, если это единственный вариант).


person veryreverie    schedule 03.05.2018    source источник
comment
Что вам сказал компилятор? Вы можете перегружать процедуры с привязкой к типу только процедурами с соответствующими аргументами, и каждый переданный фиктивный аргумент должен быть полиморфным.   -  person Vladimir F    schedule 03.05.2018
comment
Да, это. Но если я заменю type(Child) на class(Child), чтобы избежать ошибки, я больше не смогу использовать this=Child(input), а Grandchild наследует foo_Child, что мне не нужно.   -  person veryreverie    schedule 03.05.2018
comment
Термин «полиморфность» не применяется к процедурам и типам в Фортране — он применяется только к переменным. Думаю, я знаю, что вы имеете в виду, но не обязательно то, что вам нужно.   -  person IanH    schedule 03.05.2018
comment
Ах, простите, под полиморфной процедурой я подразумеваю процедуру, которая принимает class(...) :: this, а не type(...) :: this. Думаю, я использую полиморфную процедуру как сокращение для процедуры с полиморфным аргументом.   -  person veryreverie    schedule 03.05.2018
comment
Что мне нужно: я пишу обработчик ввода-вывода. Процедура object%foo(input) преобразует строку input в object. Тогда все, что расширяет Parent, может быть прочитано из строки. В модуле Parent есть куча операций с файлами и тому подобное, с кучей функций, которые принимают class(Parent) :: this в качестве аргументов. Мне нужен какой-то способ гарантировать, что каждый класс, расширяющий Parent, имеет процедуру foo, которую можно вызывать из модуля Parent.   -  person veryreverie    schedule 03.05.2018
comment
Здесь есть несколько вопросов. Простой ответ на вступительное предложение — «Нет», но на самом деле вы хотите знать, как закодировать что-то, отвечающее вашим требованиям, о которых у нас есть только беглый взгляд.   -  person IanH    schedule 03.05.2018
comment
Хорошо, я думаю, что у меня есть частичное решение: используя select type(this); type is(Child), я могу рассматривать this как неполиморфный. Это не решает проблему наследования внуком foo_Child и похоже на взлом, но пока работает. Спасибо всем.   -  person veryreverie    schedule 03.05.2018


Ответы (1)


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

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

Полиморфный объект можно преобразовать в неполиморфный с помощью SELECT TYPE и защиты типа, которая соответствует динамическому типу объекта. Вы также можете весело нарезать содержимое своего сердца с помощью ассоциации аргументов - полиморфный фактический аргумент может быть связан с неполиморфным манекеном того же объявленного типа.

Расширения можно заставить реализовать привязку, сделав родительский тип абстрактным и сделав (или оставив) привязку отложенной (как вы уже сделали). В вашей ситуации, возможно, потребуется дополнительный тип в вашей иерархии.

Parent (abstract) --> Child (abstract) +-> RealChild (concrete)
                                       |-> GrandChild (concrete)

Child в приведенном выше примере может просто оставить привязку foo отложенной или предоставить процедуру для этой привязки, а затем ввести новую отложенную привязку, которую должны реализовать RealChild и GrandChild.

person IanH    schedule 03.05.2018