メインコンテンツへスキップ

一度きりしか呼べないファンクターを管理したい

·758 文字·
技術解説 C++ SFINAE
komori-n
著者
komori-n

以下の記事の派生。

move-onlyな関数を扱えるstd::functionのようなものを実装する
·2037 文字
技術解説 C++

一度しか呼べないことが保証されたstd::functionのようなものが欲しい。ファンクターをコールできるのは一度きりで、呼んだ後は必ずデストラクトされるようにしたい。ついでに、std::functionでは保持できないmove-onlyなファンクターも保持したい。

本ページのサンプルコードは以下の場所にある。 onetime_function.hpp

以前に作った(このページ冒頭の記事)komori::unique_functionとの主な差分箇所を抜粋する。

    template <std::nullptr_t Dummy = nullptr>
    auto operator()(ArgTypes&&... args)
    -> std::enable_if_t<!std::is_same<Res, void>::value && Dummy == nullptr, Res> {
      if (storage_) {
        auto&& res = invoker_(storage_, std::forward<ArgTypes>(args)...);
        onetime_function().swap(*this);
        return std::forward<Res>(res);
      } else {
        throw std::runtime_error("storage is null");
      }
    }

    template <std::nullptr_t Dummy = nullptr>
    auto operator()(ArgTypes&&... args)
    -> std::enable_if_t<std::is_same<Res, void>::value && Dummy == nullptr, Res> {
      if (storage_) {
        invoker_(storage_, std::forward<ArgTypes>(args)...);
        onetime_function().swap(*this);
      } else {
        throw std::runtime_error("storage is null");
      }
    }

戻り値(Res)がvoidかどうかによって実装を切り替えている1。戻り値がvoid以外の場合、invokeの戻り値を中継して呼び出し元に返却する必要があるが、voidの場合は必要ない。C++ではvoid型の変数を宣言することは許されないので、SFINAEを用いて実装を切り替えている。

やっていることは単純で、関数呼び出し直後にnullファンクターとswapするだけである。入れ替え先のnullファンクターはswap直後にデストラクトされるので、storageで抱えているファンクターを2度以上呼び出すことはできない。

storageがnullの場合の処理はやや迷ったが、ここではruntime errorを上げるようにしている。

(2021/02/15追記)以下でコードを公開。

komori-n/unique-function

a simple move-only std::function replacement

C++
0
0

Related

SFINAEでtemplate classのメンバ関数の実体化を制御する
·1734 文字
技術解説 C++ SFINAE STL
move-onlyな関数を扱えるstd::functionのようなものを実装する
·2037 文字
技術解説 C++
libstdc++のstd::functionの実装を眺める
·3220 文字
技術解説 C++ STL