OPの質問の一部が実際に回答されたかどうかは明らかではありません. さらに、確かに、いくつかの明確化の恩恵を受ける可能性のあるその後の回答/議論には、多くの混乱とさまざまなエラーがあるようです.
A)OPの質問Re
「それなら、インテント (アウト) を使用する理由も知りたいです。なぜなら、インテント (インアウト) は必要な作業が少ない (データのコピーがない) からです。」
答えていないか、少なくとも直接的/正確すぎる可能性があります。
まず、明確にするために、Intent
属性には少なくとも 2 つの目的があります。「安全/衛生」と「間接的なパフォーマンス」の問題 (「直接的なパフォーマンス」の問題ではありません)。
1) 安全/衛生: 「物事を台無しにする」機会を減らし、「安全で賢明な」コードの作成を支援すること。したがって、Intent(In) を上書きすることはできません (少なくともローカルに、または場合によっては「グローバルに」、以下を参照してください)。
同様に、Intent(Out) では、Arg に「明示的な回答」を割り当てる必要があるため、「無駄な」結果を減らすのに役立ちます。
たとえば、計算数学でおそらく最も一般的な問題、つまりいわゆる「Ax=b 問題」の解では、探している「直接的な結果/答え」はベクトル x の値です。x に「明示的な」回答が割り当てられるようにするには、これらを Intent(Out) にする必要があります。たとえば、x が Intent(InOut) または「no Intent」として宣言されている場合、Fortran は x にいくつかの「デフォルト値」を割り当てます (デバッグ モードではおそらく「ゼロ」ですが、リリース モードではおそらく「がらくた」です。 Args ポインタ位置のメモリ)、ユーザーが x に正しい値を明示的に割り当てなかった場合、"ゴミ" が返されます。Intent(Out) は、ユーザーに値を明示的に x に割り当てるように「思い出させる/強制する」ことで、この種の「(偶発的な)ゴミ」を回避します。
ソリューション プロセスでは、(ほぼ確実に) 行列 A の逆行列が生成されます。ユーザーは、呼び出し元の s/r に A の代わりにその逆行列を返したい場合があります。この場合、A は Intent(InOut) である必要があります。
あるいは、行列 A またはベクトル b に変更が加えられていないことを確認したい場合があります。この場合、それらは Intent(In) と宣言され、重要な値が上書きされないようにします。
2 a) 「間接的なパフォーマンス」(および「グローバルな安全/衛生」): インテントはパフォーマンスに直接影響を与えるためのものではありませんが、間接的に影響を与えます。特に、特定の種類の最適化、特に Fortran の Pure および Elemental 構成体では、パフォーマンスが大幅に向上する可能性があります。通常、これらの設定では、すべての Args に Intent を明示的に宣言する必要があります。
大まかに言えば、コンパイラがすべての変数のインテントを事前に知っている場合、コンパイラはコードを最適化して「愚かさをチェック」し、より簡単かつ効果的に行うことができます。
重要なことに、Pure etc コンストラクトを使用する場合、Pure/Elemental s/p は他の Pure/Elemental s/p のみを呼び出すことができるため、高い確率で「一種のグローバルな安全/衛生」も得られます。 「The Glazer Guy's」の例で示されているような状況に到達することはできません。
たとえば、Sub1() が Pure として宣言されている場合、Sub2() も Pure として宣言する必要があり、すべてのレベルでインテントを宣言する必要があるため、「The Glazer Guy's " 例は起こりませんでした。つまり、コードは次のようになります。
Pure subroutine sub_P(i)
integer,intent(in) :: i
call sub2_P(i)
end subroutine sub_P
Pure subroutine sub2_P(i)
implicit none
! integer i ! not permitted to omit Intent in a Pure s/p
integer,intent(in) :: i
i = 7 ! This WILL NOT WORK/HAPPEN, since Pure obviates the possibility of omitting Intent, and Intent(In) prohibits assignment ... so "i" remains "safe".
end subroutine sub2_P
...コンパイル時に、これは次のようなものを生成します
" ||エラー: (1) の変数定義コンテキスト (代入) に INTENT(IN) を持つダミー引数 'i' |"
もちろん、sub2 は、i を Intent(In) として宣言するために Pure である必要はありません。これは、探している「安全/衛生」を提供します。
Intent(InOut) と宣言されていたとしても、Pure では失敗することに注意してください。あれは:
Pure subroutine sub_P(i)
integer,intent(in) :: i
call sub2_P(i)
end subroutine sub_P
Pure subroutine sub2_P(i)
implicit none
integer,intent(inOut) :: i
i = 7 ! This WILL NOT WORK, since Pure obviates the possibility of "mixing" Intent's.
end subroutine sub2_P
...コンパイル時に、これは次のようなものを生成します
"||エラー: (1) の変数定義コンテキスト (INTENT への実際の引数 = OUT/INOUT) 内の INTENT(IN) を持つダミー引数 'i'|"
したがって、Pure/Elemental コンストラクトへの厳密または広範な依存は、(ほとんどの場合) "グローバルな安全/衛生" を保証します。
すべての場合に Pure/Elemental などを使用できるわけではありません (たとえば、多くの混合言語設定、または制御できない外部ライブラリに依存している場合など)。
それでも、Intents を一貫して使用し、可能な限り Pure などを使用すると、多くの利点が生まれ、多くの悲しみが解消されます。
ピュアであるかどうかに関係なく、可能な限りいつでもどこでもインテントを宣言する習慣を身につけることができます...これが推奨されるコーディングプラクティスです。
...これはまた、Intent(InOut) と Intent(Out) の両方が存在する別の理由を前面に押し出します。Pure にはすべての Arg の Intent が宣言されている必要があるため、Out のみの Args がいくつかあり、他のものは InOut (つまり、In、InOut、および Out の各インテントなしで Pure を持つことは困難です)。
2 b) 「コピーが不要なため」パフォーマンスの向上を期待する OP のコメントは、Fortran の誤解と参照渡しの広範な使用を示しています。配列の最初の要素へのポインター (およびいくつかの非表示の配列情報) が必要です。
実際、配列の受け渡しが次のようにコーディングされていた「昔」(Fortran IV、77 など) を考慮すると、いくつかの洞察が得られる場合があります。
Real*8 A(1000)
Call Sub(A)
Subroutine Sub(A)
Real*8 A(1) ! this was workable since Fortran only passes the pointer/by ref to the first element of A(1000)
! modern Fortran may well throw a bounds check warning
現代の Fortran では、s/r で A を Real(DP) A(:) として宣言するのが「同等」です (ただし、厳密に言えば、配列の境界を渡し、境界を明示的に宣言することで恩恵を受けるさまざまな設定がありますが、別の日の長い余談になります)。
つまり、Fortran は値渡しも、引数/ダミー変数の「コピーの作成」も行いません。呼び出し元の s/r の A() は、s/r で使用されるものと「同じ A」です (もちろん、s/r では、A() などのコピーを作成できます。これにより、追加の仕事/スペースの要件ですが、それは別の問題です)。
大規模な配列 Arg などの場合でも、インテントがパフォーマンスに大きな影響を与えないのは主にこのためです。
B) 「値による受け渡し」の混乱について: 上記のさまざまな回答は、インテントの使用が「値による受け渡しではない」ことを確認していますが、問題を明確にすることが役立つ場合があります。
文言を「意図は常に参照渡し」に変更すると役立つ場合があります。これは「値渡しをしない」と同じではなく、重要な微妙な点です。特に、インテントは「byRef」であるだけでなく、インテントは値による受け渡しを防ぐことができます。
特別な/より複雑な設定 (たとえば混合言語の Fortran DLL など) ではさらに多くの追加の議論が必要ですが、「標準 Fortran」の大部分では、Args は Ref によって渡されます。この「意図の微妙さ」のデモンストレーションは、次のように「The Glazer Guys」の例の単純な拡張で見ることができます。
subroutine sub(i)
integer, intent(in) :: i, j
integer, value :: iV, jV
call sub2(i)
call sub3(i, j, jV, iV)
end
subroutine sub2(i)
implicit none
integer i
i = 7 ! This works since the "intent" information was lost.
end
subroutine sub3(i, j, jV, iV)
implicit none
integer, value, Intent(In) :: i ! This will work, since passed in byRef, but used locally as byVal
integer, value, Intent(InOut) :: j ! This will FAIL, since ByVal/ByRef collision with calling s/r,
! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
integer, value, Intent(InOut) :: iV ! This will FAIL, since ByVal/ByRef collision with calling s/r,
! ... in spite of "byVal" in calling s/r
! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
integer, value, Intent(Out) :: jV ! This will FAIL, since ByVal/ByRef collision with calling s/r
! ... in spite of "byVal" in calling s/r
! ||Error: VALUE attribute conflicts with INTENT(OUT) attribute at (1)|
jV = -7
iV = 7
end
つまり、呼び出し元の s/r が「byRef」を予期しているため、「Out」アスペクトを持つものはすべて「byRef」でなければなりません (少なくとも通常の設定では)。したがって、すべての s/r が Args を「値」として宣言したとしても、それらはローカルでのみ「byVal」になります (やはり標準設定で)。そのため、呼び出された s/r が任意の種類の Out Intent を持つ Value として宣言された Arg を返そうとすると、渡されたスタイルの「衝突」のために失敗します。
「Out」または「InOut」と「Value」でなければならない場合、Intent を使用することはできません。これは、単に「値渡しではない」と言っているだけではありません。