Перегрузка для разных типов и их базовый класс - PullRequest
1 голос
/ 27 апреля 2020

Предположим, у меня есть абстрактный базовый класс Shape_t с производными типами Rectangle_t и Circle_t. Существует обобщенная c функция get_area для обоих производных типов, и я хотел бы также перегрузить ее для класса, чтобы я получил следующие интерфейсы (нотация Джулиана):

get_area(type(Circle_t) :: C)
get_area(type(Rectangle_t) :: R)
! The following leads to ambiguous interfaces
get_area(class(Shape_t) :: S)

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

  1. Есть ли что-то концептуально неправильное в том, чего я хочу достичь? Поскольку переменные явно объявлены как polymorphi c (class(...)), компилятор всегда может выбрать наиболее конкретный c интерфейс и вернуться к интерфейсу polymorphi c. Поэтому я не вижу двусмысленности.

  2. Если ответ на вопрос 1: «Не существует концептуальной двусмысленности». Есть ли в стандарте планы изменить это?

  3. Приведен ли следующий код, в котором введен dyn_get_area для динамического c полиморфизма, solid обходной путь? Обратите внимание, что я хотел бы придерживаться полиморфизма stati c как можно дольше, то есть до тех пор, пока конкретная форма известна во время компиляции.

module shapes_mod
    implicit none
    private
    public :: Shape_t, Rectangle_t, Circle_t, PI, get_area, dyn_get_area

    real, parameter :: PI = atan(1.0) * 4.0

    type, abstract :: Shape_t
    end type

    type, extends(Shape_t) :: Circle_t
        real :: r = 0.0
    end type

    type, extends(Shape_t) :: Rectangle_t
        real :: a = 0.0, b = 0.0
    end type

    interface get_area
        module procedure get_area_Rectangle_t, get_area_Circle_t
    end interface

    contains

    pure function get_area_Circle_t(C) result(res)
        type(Circle_t), intent(in) :: C
        real :: res
        res = C%r**2 * PI
    end function

    pure function get_area_Rectangle_t(R) result(res)
        type(Rectangle_t), intent(in) :: R
        real :: res
        res = R%a * R%b
    end function

    pure function dyn_get_area(S) result(res)
        class(Shape_t), intent(in) :: S
        real :: res
        select type(S)
        type is(Rectangle_t)
            res = get_area(S)
        type is(Circle_t)
            res = get_area(S)
        end select
    end function

end module


program test_polymorphic_and_static_overload
    use shapes_mod, only: Shape_t, Rectangle_t, Circle_t, get_area, dyn_get_area
    implicit none
    class(Shape_t), allocatable :: random_shape
    type(Circle_t) :: circle
    type(Rectangle_t) :: rectangle
    real :: p

    circle = Circle_t(1.0)
    rectangle = Rectangle_t(1.0, 2.0)

    call random_number(p)
    if (p < 0.5) then
        random_shape = circle
    else
        random_shape = rectangle
    end if

    write(*, *) get_area(circle)
    write(*, *) get_area(rectangle)
    write(*, *) dyn_get_area(random_shape)
end program

1 Ответ

4 голосов
/ 28 апреля 2020

Правила Fortran для выбора определенной c процедуры из универсального c интерфейса и того, как определенные c процедуры в универсальном c интерфейсе должны быть различимы, предназначены для простого изучения и применения , Чтобы избежать необходимости в каких-либо правилах, описывающих, как выбирается «наиболее подходящее» / «наиболее конкретное c», в обстоятельствах, которые в противном случае могут быть неоднозначными, языковые правила таковы, что может быть не более одной неэлементарной процедуры и не более одной элементарной процедуры, которая соответствует, в любой единице определения объема (элементная специфика рассматривается только в том случае, если неэлементарный специфика c не совпадает).

Предлагаемые вами процедуры нарушают эти языковые правила.

Я не видел ни одного реалистичного c предложения по изменению правил таким образом, чтобы было бы утрачено намерение разработки "простое в освоении и применении".

Установите отложенную привязку в каждом типе это назначает функцию, которая реализует соответствующие вычисления для get_area. Перейдите к этой привязке из функции get_shape (или измените ссылки на get_shape, чтобы они были ссылками на привязку). Избегайте реализации поведения, определяемого типом c, с помощью SELECT TYPE - поведение, определяемое типом c, должно реализовываться с использованием привязок.

...