🧌
畏れずに値を返そう
以下のC++の戻り値のある関数を見てみましょう。
struct Data {
std::string name;
int age;
long points;
};
Data createData() {
return Data{ "Taro", 30, 1500 }; // コピーするかもしれない
}
int main() {
Data data = createData(); // コピーするかもしれない
}
C++11前は言語のロジックとして、CreateData()
の中で作られたData
のインスタンスが戻り値として返される時にコピーされて、main*()
でdata
にもう一回コピーされるようになっていました。そういう余計なコピーを避けるために、多くのプログラマーは以下のように関数を書くようになりました。
void createData(Data& data) {
data = {"Taro", 30, 1500};
}
int main() {
Data data;
createData(data);
}
このように出力引数を使うと、直接data
を変えられるため、余計なコピーは避けられます。参照型の代わりにポインタ型の引数が使われる関数も結構見かけます。
でもこういうことしないといけなかったのは90年代の話です。
C++11前でも殆どのコンパイラはReturn Value Optimization (RVO)
という効率化を使って戻り値のコピーを避けてくれていました。
C++11でムーブコンストラクタが存在する限り、言語のロジックとして戻り値がコピーされずにムーブされるようになりました。
でも、もう安心してください!C++17でもうムーブコンストラクタがなくても戻り値がコピーされることは無くなりました。戻り値以外のケースでも余計なコピーは発生しなくなっています。
なので、今時コピーを避けるために出力引数を使う必要はないです。どんなにでかい構造体でもコピーを恐れずに直接戻り値として返しましょう。
Discussion
コピーもムーブもされません。 直接初期化 (direct initialization) 相当の挙動になります。
コメントありがとうございます。
元の文章は「コピーされない」に注目したもので「ムーブであると限定したいわけではない」というものでした。
ご指摘通りのムーブへの限定は誤解を招くものとなっていましたので、表現を訂正しました。