以下の記事の派生。

一度しか呼べないことが保証された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追記)以下でコードを公開。



