potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20&MSVC 範囲for文でco_yieldを使う場合は参照で受け取る

MSVCはC++20でもジェネレーターが使えるようにexperimental機能としてstd::experimental::generator<T>を提供しています。このクラスを使うとco_yieldで簡単にジェネレーターを実装できますが、範囲for文と組み合わせる場合は参照で受け取った方が良さそうです。参照で受け取らない場合、デストラクタが繰り返し呼び出されました。

// C++20
// プロジェクトプロパティ→C/C++→コマンドライン→追加のオプションに「/await」を追加してください。

#include <iostream>
#include <experimental/generator>
#include <ranges>

class coroutine_test
{
public:
    coroutine_test() { std::wcout << L"coroutine_test()" << std::endl; }
    ~coroutine_test() { std::wcout << L"~coroutine_test()" << std::endl; }
};

std::experimental::generator<coroutine_test> f()
{
    auto co = coroutine_test();

    co_yield co;
    co_yield co;
    co_yield co;
    co_yield co;
    co_yield co;
}

int main()
{
    // 参照で受け取る
    for (auto& co : f() | std::views::take(3))
    {
        std::wcout << L"for" << std::endl;
    }
    // coroutine_test()
    // for
    // for
    // for
    // ~coroutine_test()

    std::wcout << std::endl;

    // 参照で受け取らない
    for (auto co : f() | std::views::take(3))
    {
        std::wcout << L"for" << std::endl;
    }
    // coroutine_test()
    // for
    // ~coroutine_test()
    // for
    // ~coroutine_test()
    // for
    // ~coroutine_test()
    // ~coroutine_test()

    return 0;
}