Visual Studio 2019 Community Version 16.7.0 Preview 2.0で関数の戻り値のコンストラクタ呼び出しとRVOを確認した結果です。C++言語標準はstd:c++latest(C++20 draft)です。
関数の戻り値となるクラスのコンストラクタの優先順位は「ムーブコンストラクタ→コピーコンストラクタ→通常のコンストラクタ」であることが確認できました。コードの複雑さにもよるかもしれませんが、関数をそのまま返してもRVOやムーブコンストラクタが選択されているのでstd::move
は省略できるかもしれません。主にポインタの書き換えなので対した処理ではないですが。
方法 | Debug構成 | Release構成 |
---|---|---|
コンストラクタをそのまま返す。 | RVO | RVO |
関数内のローカル変数をそのまま返す。 | ムーブコンストラクタ | NRVO |
関数内のローカル変数をstd:moveして返す。 | ムーブコンストラクタ | ムーブコンストラクタ |
別の関数の戻り値をそのまま返す。 | RVO | RVO |
別の関数の戻り値をstd:moveして返す。 | ムーブコンストラクタ | ムーブコンストラクタ |
用語
・RVO:Return Value Optimization
・NRVO:Named Return Value Optimization
テストコード
#include <string>
#include <iostream>
using namespace std;
class TestClass
{
private:
static int counter;
int index;
public:
wstring value;
// デフォルトコンストラクタ
TestClass()
: value(L"<default>"), index(counter++)
{
wcout << L"TestClass() : " << index << endl;
}
TestClass(wstring_view sv)
: value(sv), index(counter++)
{
wcout << L"TestClass(wstring_view sv) : " << index << endl;
}
// コピーコンストラクタ
TestClass(const TestClass& source)
: value(source.value), index(counter++)
{
wcout << L"TestClass(const TestClass& source) : " << index << endl;
}
// ムーブコンストラクタ
TestClass(TestClass&& source)
: value(move(source.value)), index(counter++)
{
wcout << L"TestClass(TestClass&& source) : " << index << endl;
}
~TestClass()
{
wcout << L"~TestClass() : " << index << endl;
}
};
int TestClass::counter = 0;
TestClass f1()
{
return TestClass(L"abc");
}
TestClass f2()
{
TestClass tc = TestClass(L"abc");
return tc;
}
TestClass f3()
{
TestClass tc = TestClass(L"abc");
return move(tc);
}
TestClass f4()
{
return f1();
}
TestClass f5()
{
return move(f1());
}
int main()
{
// コンストラクタをそのまま返す。
{
auto tc1 = f1();
wcout << tc1.value << endl;
}
wcout << endl;
// 関数内のローカル変数をそのまま帰す。
{
auto tc2 = f2();
wcout << tc2.value << endl;
}
wcout << endl;
// 関数内のローカル変数をstd:moveして帰す。
{
auto tc3 = f3();
wcout << tc3.value << endl;
}
wcout << endl;
// 別の関数の戻り値をそのまま返す。
{
auto tc4 = f4();
wcout << tc4.value << endl;
}
wcout << endl;
// 別の関数の戻り値をstd:moveして返す。
{
auto tc5 = f5();
wcout << tc5.value << endl;
}
return 0;
}
Debug構成の出力
TestClass(wstring_view sv) : 0
abc
~TestClass() : 0
TestClass(wstring_view sv) : 1
TestClass(TestClass&& source) : 2
~TestClass() : 1
abc
~TestClass() : 2
TestClass(wstring_view sv) : 3
TestClass(TestClass&& source) : 4
~TestClass() : 3
abc
~TestClass() : 4
TestClass(wstring_view sv) : 5
abc
~TestClass() : 5
TestClass(wstring_view sv) : 6
TestClass(TestClass&& source) : 7
~TestClass() : 6
abc
~TestClass() : 7
Release構成の出力
TestClass(wstring_view sv) : 0
abc
~TestClass() : 0
TestClass(wstring_view sv) : 1
abc
~TestClass() : 1
TestClass(wstring_view sv) : 2
TestClass(TestClass&& source) : 3
~TestClass() : 2
abc
~TestClass() : 3
TestClass(wstring_view sv) : 4
abc
~TestClass() : 4
TestClass(wstring_view sv) : 5
TestClass(TestClass&& source) : 6
~TestClass() : 5
abc
~TestClass() : 6