Как переопределить конструктор структуры в fortran

возможно ли в настоящее время переопределить конструктор структуры в Fortran? Я видел такие примеры (например, в спецификации Fortran 2003):

module mymod

  type mytype
    integer :: x
    ! Other stuff
  end type

  interface mytype
    module procedure init_mytype
  end interface

contains
  type(mytype) function init_mytype(i)
    integer, intent(in) :: i
    if(i > 0) then
      init_mytype%x = 1
    else
      init_mytype%x = 2
    end if
  end function
end

program test
  use mymod
  type(mytype) :: x
  x = mytype(0)
end program

это в основном генерирует кучу ошибок из-за избыточных имен переменных (например, ошибка: производный атрибут "mytype" конфликтует с атрибутом процедуры в (1)). Стенографическая копия примера fortran 2003 создает аналогичные ошибки. Я пробовал это в gfortran 4.4, ifort 10.1 и 11.1, и все они производят те же ошибки.

мой вопрос: это просто нереализованная функция fortran 2003? Или я неправильно это реализую?

Edit: я наткнулся на сообщить об ошибке и анонсирован патч gfortran по этому вопросу. Тем не менее, я попытался использовать ноябрьскую сборку gcc46 без везения и подобных ошибок.

Edit 2: приведенный выше код работает с использованием Intel Fortran 12.1.0.

2 ответов


я сверился со своей копией стандарта Fortran 2008. Что позволяет определить универсальный интерфейс с тем же именем в производном типе. Мой компилятор (Intel Fortran 11.1) не будет компилировать код, хотя я подозреваю (без копии стандарта 2003), что это еще не реализованная функция стандарта Fortran 2003.

кроме того, в вашей программе есть ошибка. Ваше объявление функции:

  type(mytype) function init_mytype
    integer, intent(in) :: i

указывает существование и намерение аргумента, которого нет в спецификации функции, которое, возможно, следует переписать как:

  type(mytype) function init_mytype(i)

возможно ли в настоящее время переопределить конструктор структуры в Fortran?

нет. в любом случае даже использование вашего подхода полностью не связано с переопределением конструктора. Основная причина заключается в том, что конструктор структуры # OOP конструктор. Есть некоторое сходство, но это просто другая идея.

вы не можете использовать свою не-внутреннюю функцию в выражении инициализации. Вы можете использовать только константу, массив или структуру конструктор, встроенные функции, ... Для получения дополнительной информации взгляните на выражение инициализации 7.1.7 в Fortran 2003 draft.

принимая во внимание этот факт, я полностью не понимаю, в чем реальная разница между

type(mytype) :: x
x = mytype(0)

и

type(mytype) :: x
x = init_mytype(0)

и в чем весь смысл использования интерфейсного блока внутри модуля mymod.

ну, честно говоря, есть разница, огромная - первый способ вводит в заблуждение. Этот функция не является конструктором (поскольку в Fortran вообще нет конструкторов ООП), она является инициализатором.


в основном конструкторе ООП отвечает за последовательное выполнение двух вещей:

  1. выделение памяти.
  2. инициализации членов.

давайте рассмотрим некоторые примеры создания экземпляров классов на разных языках.

In Java:

MyType mt = new MyType(1);

очень важный факт скрыт-тот факт, что объект на самом деле является указателем на varibale типа класса. Эквивалент в C++ будет распределение по куче использование:

MyType* mt = new MyType(1);

но на обоих языках можно видеть, что две обязанности конструктора отражены даже на уровне синтаксиса. Он состоит из двух частей: ключевое слово new (выделение) и имя конструктора (инициализация). В С синтаксис этот факт еще более подчеркивал:

MyType* mt = [[MyType alloc] init:1];

много раз, однако, вы можете увидеть некоторую другую форму вызова конструктора. В случае распределение по стеку C++ использует специальную (очень плохую) синтаксическую конструкцию

MyType mt(1);

что на самом деле настолько вводит в заблуждение, что мы можем просто не рассматривать его.

на Python

mt = MyType(1)

как на самом деле объект на самом деле является указателем и то, что выделение происходит первым, скрыто (на уровне синтаксиса). И этот метод называется ... __init__! O_O так вводит в заблуждение. Выделение стека С++ блекнет в сравнении с этим. =)


в любом случае, идея конструктор в языке подразумевают способность делать выделение инициализации в одном операторе используя какой-то специальный метод. И если вы думаете, что это" истинный ОП", у меня для вас плохие новости. Даже Smalltalk не имеет конструкторов. Это просто конвенция, чтобы иметь new метод на самих классах (они являются одноэлементными объектами мета-классов). The Шаблон Проектирования Фабрики используется во многих других языках для достижения той же цели.

я где-то читал, что концепции модулей в Fortran были вдохновлены Modula-2. И мне кажется, что функции ООП вдохновлены Оберон-2. Нет конструкторов в Оберон-2 также. Но есть, конечно, чистое распределение с предшествующей процедурой NEW (например, ALLOCATE в Fortran, но ALLOCATE is statement). После выделения вы можете (должны на практике) вызвать некоторый инициализатор, который является обычным методом. Ничего особенного.

таким образом, вы можете использовать какие-то фабрики для инициализации объектов. Это то, что вы на самом деле сделали, используя модули вместо одноэлементных объектов. Или лучше сказать, что они (Java/C#/... программисты) используют одноэлементные объекты методы вместо обычных функций из - за отсутствия более позднего (нет модулей-нет способа иметь обычные функции, только методы).

также вы можете использовать подпрограмму с привязкой к типу.

MODULE mymod

  TYPE mytype
    PRIVATE
    INTEGER :: x
    CONTAINS
    PROCEDURE, PASS :: init
  END TYPE

CONTAINS

  SUBROUTINE init(this, i)
    CLASS(mytype), INTENT(OUT) :: this
    INTEGER, INTENT(IN) :: i

    IF(i > 0) THEN
      this%x = 1
    ELSE
      this%x = 2
    END IF
  END SUBROUTINE init

END

PROGRAM test

  USE mymod

  TYPE(mytype) :: x

  CALL x%init(1)

END PROGRAM

INTENT(OUT) на this арг о init подпрограмма, кажется, в порядке. Потому что мы ожидаем, что этот метод будет вызываться только один раз и сразу после выделения. Возможно, было бы неплохо контролировать, что это предположение не будет неправильным. Чтобы добавить логический флаг LOGICAL :: inited to mytype, проверьте, если это .false. и установить его в .true. при первой инициализации, и сделать что-то еще при попытке повторной инициализации. Я определенно помню некоторые темы об этом в группах Google... Я не могу его найти.