LoginSignup
3
0

More than 3 years have passed since last update.

C++ 関数の戻り値のコンストラクタ呼び出しとRVO(Visual Studio 2019 Community 16.7.0 Preview 2.0)

Last updated at Posted at 2020-06-06

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
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0