「値」と「参照」、「型」と「渡し」
「値型」「参照型」「値渡し」「参照渡し」、ついでにC++の言語規格で定義されている「参照」という言葉は、その字面からごっちゃになりがちなので、ここに簡単にまとめておく。
値型、参照型
値型(value type)、参照型(reference type)というのはプログラミングにおけるデータ型(data type)の分類の一種である。
値型のオブジェクトは「実体としてのデータ」を持ち、参照型のオブジェクトは「実体を参照するためのデータ(アドレスとか)」を持つ。
C++では「ポインタ」「スマートポインタ」がプログラミング一般における「参照型」に当たる。
型 | 値型か参照型か |
---|---|
int |
値型 |
int* |
参照型 |
shared_ptr<int> |
参照型 |
値渡し、参照渡し
値渡し(pass by value)、参照渡し(pass by reference)というのは評価戦略(evaluation strategy)の用語で、関数の仮引数(formal parameter)と実引数(actual argument)の関係を表す。
値渡しでは仮引数は実引数のコピーとなり、関数の中で仮引数に対して行った操作は実引数に影響を及ぼさない。
参照渡しでは仮引数は実引数のエイリアスとなり、仮引数に対して操作を行うことは実引数に対して操作を行うことと同じになる。
C++における仮引数のデータ型と評価戦略の組み合わせは以下の通り。
C++では仮引数にそのものずばり「参照」を使った場合に参照渡しとなる。
関数宣言 | 仮引数の型 | 評価戦略 | 自分が使う呼び方 |
---|---|---|---|
void f(int a) |
値型 | 値渡し | 値渡し |
void f(int* a) |
参照型 | 値渡し | (生)ポインタ渡し |
void f(shared_ptr<int> a) |
参照型 | 値渡し | - |
void f(int& a) |
値型 | 参照渡し | 参照渡し |
void f(const int& a) |
値型 | 参照渡し | const参照渡し |
void f(const shared_ptr<int>& a) |
参照型 | 参照渡し | (スマート)ポインタ(のconst参照)渡し |
以上の組み合わせのうち、比較的ややこしい「参照型の値渡し」と「参照型の参照渡し」について以下に補足する。
参照型の値渡し
参照型の値渡しでは、関数の中から実引数が参照していた実体を仮引数を通じて操作できるが、実引数そのものは操作できない。
C++のポインタ渡しについては、たとえば int a; f(&a);
などは「関数 f()
の中で実引数 a
を操作できるから値型の参照渡しでは?」と思われるかもしれないが、この場合の実引数は &a
の結果であるポインタ型の一時オブジェクトであってその一時オブジェクトを仮引数を通じて操作することはできないので、「参照型の値渡し」になる。
ちなみに「参照渡し」という言葉については、時に「参照型の値渡し」という言葉が「参照(型の値)渡し」→「参照渡し」のように略して使われている場合があるので、適宜正しく読み解く必要がある。
参照型の参照渡し
参照型の参照渡しでは、関数の中から実引数が参照している実体を仮引数を通じて操作できるし、実引数そのものも操作できる。
C++の場合、「関数が実引数に副作用を起こさないことを明確にする上では『値型の値渡し』の形式がベストなのだけれどもコピーコストが無視できないので値渡しは避けたい」というときに使う「const参照渡し」が、参照渡しのもっともメジャーな使い方だと思われる。