如何在抽象派生类型中设计 PRIVATE OVERRIDABLE 过程?

Posted

技术标签:

【中文标题】如何在抽象派生类型中设计 PRIVATE OVERRIDABLE 过程?【英文标题】:How to design a PRIVATE OVERRIDABLE procedure in an abstract derived-type? 【发布时间】:2018-06-09 22:35:12 【问题描述】:

一般来说,抽象类型是后代派生类型的模型。如果类型中包含的过程被推迟,则该类型中的过程应该是 PUBLIC,因为 PRIVATE 不能被模块本身引用或覆盖。但是,如果我希望每个后代都有相同的功能 PRIVATE 过程来做某事(例如,测试它的实例的初始化状态),我该如何设计 ABSTRACT 派生类型?我是否必须在每个后代中手动定义它们,或者我可以将模型放在抽象中并在后代中实现它? 这是一个例子:

module test1
    implicit none

    private
    public :: my_numeric_type

    type, abstract :: my_numeric_type
    contains
        private
        procedure(op2), deferred :: add
        procedure(op2), deferred :: subtract
        generic, public :: operator(+) => add
        generic, public :: operator(-) => subtract
    end type my_numeric_type

    abstract interface
        function op2(a, b) result(r)
            import :: my_numeric_type
            class(my_numeric_type), intent(in) :: a, b
            class(my_numeric_type), allocatable :: r
        end function op2
    end interface
end module test1

module test2
    use test1, only: my_numeric_type
    implicit none

    type, extends(my_numeric_type) :: my_integer
        private
        integer :: value
    contains
    private
        procedure, public :: add => add_my_integer
        procedure, public :: subtract => subtract_my_integer
    end type my_integer

    contains

    function add_my_integer(a, b) result(r)
        class(my_integer),      intent(in)  :: a
        class(my_numeric_type), intent(in)  :: b
        class(my_numeric_type), allocatable :: r
        ! DO SOME ADDITION.
    end function add_my_integer

    function subtract_my_integer(a, b) result(r)
        class(my_integer),      intent(in)  :: a
        class(my_numeric_type), intent(in)  :: b
        class(my_numeric_type), allocatable :: r
        ! DO SOME SUBTRACTION.
    end function subtract_my_integer

end module test2

在示例中,后代派生类型 my_integer 是非抽象类型并继承了延迟绑定,因此必须重写过程(加法和减法)。但是程序在my_numeric_type中定义为private,也就是说它们不能在模块test1之外被覆盖,所以上面的my_integer类型是无效的。我该怎么办?

【问题讨论】:

线程已更新。非常感谢,@Vladimir。 我觉得这和***.com/questions/47959048/…有很多共同点,可以重复吗? @VladimirF,我看不出这个问题和那个问题之间有什么联系。 @Vladimir,在您建议的线程中,两种派生类型中的类型绑定过程都定义为默认访问权限为 PUBLIC。但我遇到的问题是实现 PRIVATE 延迟类型绑定过程。 【参考方案1】:

来自 fortran 2018 标准草案,第 7.5.5 节,第 9 段:

可以通过该类型的任何可访问对象访问公共类型绑定过程。私有类型绑定过程只能在包含类型定义的模块及其后代中访问。

这意味着可以实现privatedeferred 过程,但前提是实现的过程与延迟过程在同一模块中声明。

例如:

module m
  implicit none
  
  type, abstract :: Parent
  contains
    procedure(add_Parent), deferred, private :: add
    generic, public :: operator(+) => add
  end type
  
  abstract interface
    function add_Parent(lhs,rhs) result(output)
      import :: Parent
      class(Parent), intent(in)  :: lhs
      class(Parent), intent(in)  :: rhs
      class(Parent), allocatable :: output
    end function
  end interface
  
  type, extends(Parent) :: Child
    integer :: contents
  contains
    procedure, private :: add => add_Child
  end type
  
contains

function add_Child(lhs,rhs) result(output)
  class(Child),  intent(in)  :: lhs
  class(Parent), intent(in)  :: rhs
  class(Parent), allocatable :: output
  
  select type(rhs); type is(Child)
    output = Child(lhs%contents+rhs%contents)
  end select
end function

end module

program p
  use m
  
  type(Child)                :: a,b
  class(Parent), allocatable :: c
  
  a = Child(3)
  b = Child(5)
  c = a+b
  
  select type(c); type is(Child)
    write(*,*) c%contents
  end select
end program

这个约束(ParentChild 必须在同一个模块中声明)可能会导致大文件,并可能导致依赖排序问题。有几种方法可以改善这种情况:

使用submodules 分解模块。例如Parent 可以有自己的子模块,每个Child 可以有自己的子模块。 不要使用private 延迟过程,而是使用应该被视为私有的延迟过程的命名约定,例如尾随下划线(add_Parent_add_Child_)。

【讨论】:

以上是关于如何在抽象派生类型中设计 PRIVATE OVERRIDABLE 过程?的主要内容,如果未能解决你的问题,请参考以下文章

覆盖派生模板类的返回类型

如何在QML应用中设计一个C++ Model并使用它

如何在具有并发控制的 .NET 和 EF 应用程序中设计持久层?

如何派生抽象模板类,模板类型作为函数参数 (C++11)

多态重载 - 如何强制抽象类型“充当”其派生类型之一以实现重载参数?

如何在 CodeIgniter 中设计用户友好的 URI?