std::function
は、指定の引数リスト・戻り値型を持つ任意の関数オブジェクトを格納できる……これは実は真ではない。
C++ではstd::function
に入れられない関数オブジェクトというものが存在する。
具体的にはコピー不可能な関数オブジェクトは格納できない。 std::function
は格納する関数オブジェクトにコピーコンストラクト可能であることを要請する。
例として、以下のようなラムダ式はもうアウトである。
auto hoge = std::make_unique<int>(10);
auto f = [a = std::move(hoge)](){}
std::function<void()> func = std::move(f); // エラー!
unique_ptr
はコピー不可の型である。それをムーブキャプチャさせたラムダ式は当然unique_ptr
の所有権を持っており、これもまたコピー不可能になる。そのためコピー可能性を要求するstd::function
には格納できない。 これが例えばshared_ptr
ならコピー可能なのでエラーは起きなくなる。
なぜコピー可能性などが求められるのか。それはstd::function
型がコピー可能だからだ。
std::function<void()> f = [](){};
std::function<void()> g = f; // コピー
std::function
がコピー可能であるためには中身もコピー可能である必要がある。それがこの仕様の理由である。
ところで、この関数オブジェクトf
はムーブなら可能なはずである。実際、以下のコードは普通に通る。
auto hoge = std::make_unique<int>(10);
auto f = [a = std::move(hoge)](){} // fはコピー不可
auto g = std::move(f); // ムーブなら可能
結局上記の問題はstd::function
という型がコピー可能であることを保証する仕様に起因している。ならば、使う側としてはムーブしかしないからコピー不可な関数オブジェクトも入れさせてほしいという話になってくる。そこで出てくるのがC++23のstd::move_only_function
である。
auto hoge = std::make_unique<int>(10);
auto f = [a = std::move(hoge)](){}
std::move_only_function<void()> func = std::move(f); // 可能
std::move_only_function
はその名の通りムーブしかできない。その代わりに格納するオブジェクトにもムーブ可能性しか求めない。有用な場面は多いだろう。
そもそもstd::function
を用いるユースケースにおいてコピー可能性が求められることはそれほど多くないのではないかと思う。ただの変数よりもconst変数、const変数よりもconstexprという流れがあったように、C++23の時代になれば「特に理由が無いのならstd::function
よりもstd::move_only_function
」みたいなことが起きるかもしれない?