[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GNU C++ は、C++ のプログラムでは、関数定義構文を拡張して、 関数の結果を入れる名前を関数定義本体の外側で指定できるようにしている。 (訳注: これに相当する機能が最近制定された C++ 規格に入っているようである。)
type functionname (args) return resultname; { … body … } |
この機能を使うことで、関数の戻り値がクラス型の場合の余分なコンストラクタ
呼び出しを避けることができる。
例えば、関数 m
が、‘X v = m ();’ と宣言されていており、
その結果がクラス X
とする。
X m () { X b; b.a = 23; return b; } |
m
には引数が無いように見えるが、実際には暗黙の引数が一つある。
戻り値のアドレスである。呼び出し時に、v
を保持するのに充分な
スペースを指すアドレスが暗黙の引数として渡される。
次に、b
がコンストラクタにより構築され、その a
フィールド
に値 23 が設定される。
最後に、コピーコンストラクタ(‘X(X&)’ という形式のコンストラクタ)が
b
に適用され、コピー先は暗黙の戻り値位置になり、
v
がようやく戻り値に結合される。
だが、これは無駄である。ローカルの b
は、すぐコピーされていって
しまう何かを保持するために宣言されているにすぎない。
「省略」(elision) アルゴリズムと手続間データフロー解析を組み合わせた
コンパイラなら、おそらくこういうものを全部消去することができるだろうが、
戻り値を明示的に操作することで、効率の良いコードを生成するよう
ユーザがコンパイラを支援するほうがずっと実用的だろう。
これにより、ローカル変数とコピーコンストラクタを完全に避けることができる。
GNU C++ の拡張関数定義構文を使うと、
最初に戻り値に r
という名前を付け、その a
フィールドに
直接代入することで、一時的な割当とコピーを避けることができる。
X m () return r; { r.a = 23; } |
r
の宣言は標準的で適切な宣言である。
この効果は、m
の本体のどの部分よりも 前に実行される。
この型の関数は他には何も制限を付け加えない。
特に、return
文を実行可能だし、関数本体の終端に達する(「端から
落ちる」)ことで暗黙に戻ることも可能である。
X m () return r (23); { return; } |
この場合、あるいは ‘X m () return r (23); {}’ としても、
曖昧さはない。戻り値 r
はどちらの場合も初期化済みだからである。
以下の書き方は読みにくいかも知れないが、これもまた予測できる動作をする。
X m () return r; { X b; return b; } |
r
で示される戻り値スロットが最初に初期化されるが、
‘return b;’ という文でこの値は上書きされる。
コンパイラは r
を破壊する(デストラクタがあればそれを呼び出し、
なければ何もしない)ことでこれに対処し、次に r
を b
で
再初期化する。
この拡張は主に多重定義された演算子を使う人を助けるために提供している。 多重定義演算子の場合は、単に引数だけでなく、関数の戻り値を 制御するという需要が多いのである。 コピーコンストラクタの性能ペナルティが大きい(特に良くあるのが、 デフォルトのコンストラクタは高速である場合)クラスの場合は、 大きな助けになる。 この拡張の不利な点は、戻り値に対するデフォルトのコンストラクタが いつ呼ばれるかを制御しないことにある。 常に先頭で呼び出される。
This document was generated
using texi2html 1.78.