C++で関数引数をconst参照にするか値渡しにするか
C++で関数引数を書くとき、const T& にするか値渡しにするか迷うことがあります。
最初は、関数がその値をどう使うかで分けると整理しやすいです。
読むだけならconst参照
大きめのオブジェクトを読むだけなら、const T& がよく使われます。
#include <string>
void printName(const std::string& name) {
// nameを読むだけ
}
コピーを避けつつ、関数内で変更しないことを表せます。
小さい型は値渡しでよい
int や double のような小さい型は、値渡しで十分です。
int add(int a, int b) {
return a + b;
}
無理に const int& にしても読みづらくなるだけです。
int add(const int& a, const int& b); // これはやりすぎ
関数内で保持するなら値渡しも候補
引数をメンバに保存する場合は、値渡しして std::move する形もあります。
class User {
public:
explicit User(std::string name)
: name_(std::move(name)) {}
private:
std::string name_;
};
呼び出し側が右辺値を渡した場合はムーブしやすく、左辺値を渡した場合はコピーされます。
std::move の注意点は C++でstd::moveを使うときに気をつけること に分けています。
変更したいなら非const参照
関数内で引数を書き換えるなら、非const参照を使います。
void normalize(std::string& name) {
// nameを書き換える
}
ただし、呼び出し側から見ると値が変わるので、使いどころは絞ります。
戻り値で返せるなら、戻り値にした方が読みやすいこともあります。
まとめ
関数引数は、用途ごとに分けると判断しやすいです。
- 小さい型は値渡し
- 大きい型を読むだけなら
const T& - 関数内で保持するなら値渡しして
std::move - 書き換えるなら
T& - 所有権を渡すならスマートポインタやムーブを考える
全部を const T& にするのではなく、関数が何をするかを引数の型に出すと読みやすくなります。


