前置き
Fortran 90 以降、サブルーチン・関数の副プログラムの仮引数 (dummy argument) に関して、intent 指定が出来るようになっています。その種別は intent(in), intent(in out), intent(out) の三種類で、intent(in) は引数を受け取るのみ、intent(in out) は引数を受け取って呼び出し側に返す、intent(out) は引数を返すのみ、となっています。
表面的には、intent(in) 属性の仮引数は副プログラム内で内容を変更してはならず、intent(out) 属性の仮引数はまず値を代入されなければならないという文法的なチェックがかかるだけに見えます。intent(in out) の時は、指定してもしなくても同じことのようにも思えます。
しかしながら、Fortran は参照呼出し(call by reference)になっているため、もうちょっと微妙な効能があります。
FORTRAN 77 時代の値呼び出し(call by value)もどき
Fortran がスカラーも配列等オブジェクトも参照呼出しになっているのは、FORTRAN から引き継いだものだと思います。(Fortran 2003 以降では value 属性を付けることで値呼び出しにも出来ます。)
参照渡しですので、副プログラム側で仮引数を書き換えると、呼び出し側の変数そのものが変化します。でもたまには値渡し的に、呼び出し側の変数を書き換えないで、副プログラム側で自由にいじれる写しが欲しいこともあります。
FORTRAN 77 では怪しげなテクニックでこれを実現する方法が知られていました。
subroutine tstsub(x)
x = x * 2
end
program hello
a = 3.0
print *, a
call tstsub(a)
print *, a
call tstsub((a))
print *, a
end
実引数(actual argument)を括弧でくくってやると、これが式として評価され仮の番地に入れられた(一種の無名変数というか)あとでその番地が参照として渡されるため、元の変数の中身は変化しないというものです。
以下の例では、二回目の呼び出しでは変数 a の値が変化していません。
3.000000
6.000000
6.000000
一時期 java 界隈で参照の値呼び出しとか謎のワードが飛び交っていましたが、こちらも値の参照呼び出しというか異常なテクニックです。最適化しても成り立つのかよく分かりません。
他にも参照呼出しで問題となるのは、関数を実引数に置くときで、仮引数に代入があるときどうなるか不明です。77 の規格では未定義だと聞いたような気がします(調べてませんw)昔のおぼろげな記憶では、富士通 FACOM は文法エラー、日立 HITAC は実行時に暴走、日電 ACOS は大丈夫だったような気がします。DEC の VAX も OK で その流れを組む intel fortran も値呼び出し風に動きます。
function func(x)
func = x * 3
end
subroutine tstfun(x)
x = x * 2
print *, x
end
program hello
a = 3.0
print *, a
call tstfun(func(a))
print *, a
call tstfun(func(a))
print *, a
end
3.000000
18.00000
3.000000
18.00000
3.000000
しかし、intent 属性を付ければこれらの問題は解決します。
intent の効能
intent(in out) がついていると、上記のような場合コンパイル時にエラーが出ます。しかし intent 指定を付けないと 77 時代的な(多分処理系依存の)挙動をします。
なお read only 的に値を受け取るだけで書き換えないの場合は intent(in) を付ければ、式や関数を実引数においてもエラーは出ず、安心して呼び出せます。
関数呼び出し例
module test_m
implicit none
contains
pure real function func(x)
real, intent(in) :: x
func = x * 3
end function func
subroutine test_func(x)
real, intent(in out) :: x
! real :: x
x = x * 2
print *, x
end subroutine test_func
end module test_m
program hello
use test_m
real :: a = 3.0
print *, a
call test_func(func(a))
print *, a
call test_func(func(a))
print *, a
end program hello
intent(in out) 指定あり コンパイルエラー gfortran7
$ gfortran testt2.f90
testt2.f90:21:25:
call test_func(func(a))
1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)
testt2.f90:23:25:
call test_func(func(a))
1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)
intent 指定なし
hp8@HP8:~$ ./a.out
3.00000000
18.0000000
3.00000000
18.0000000
3.00000000
また intent は最適化を助ける冗長な情報を与えるので、これ以外の効能もあります。