次のような関数があるとしますstd::function
:
void callFunction(std::function<void()> x)
{
x();
}
x
代わりに const-reference で渡す必要がありますか?
void callFunction(const std::function<void()>& x)
{
x();
}
std::function
この質問に対する答えは、関数がそれに対して何を行うかによって変わりますか? たとえば、データ メンバーに格納または初期化するメンバー関数またはコンストラクターの場合です。
ベストアンサー1
パフォーマンスが必要な場合は、値を保存するときに値を渡します。
「UI スレッドでこれを実行する」という関数があるとします。
std::future<void> run_in_ui_thread( std::function<void()> )
これは「ui」スレッドでいくつかのコードを実行し、future
完了したらシグナルを送ります。(UI スレッドが UI 要素を操作する場所である UI フレームワークで役立ちます)
私たちが検討している署名は 2 つあります。
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
さて、これらを次のように使用することになります。
run_in_ui_thread( [=]{
// code goes here
} ).wait();
これにより、匿名クロージャ (ラムダ) が作成され、std::function
そこからが構築され、run_in_ui_thread
関数に渡されて、メイン スレッドで実行が完了するまで待機します。
(A) の場合、 はstd::function
ラムダから直接構築され、 内で使用されますrun_in_ui_thread
。 ラムダはmove
に組み込まれるstd::function
ため、移動可能な状態はすべて に効率的に導入されます。
2 番目のケースでは、一時オブジェクトstd::function
が作成され、そこにラムダがmove
代入され、その一時オブジェクトがstd::function
内で参照として使用されますrun_in_ui_thread
。
ここまでは順調です。2 つの関数は同じように動作します。ただし、 はrun_in_ui_thread
関数の引数のコピーを作成し、UI スレッドに送信して実行します (処理が完了する前に が返されるため、 への参照を使用することはできません)。ケース (A) では、 を長期ストレージにコピーするだけです。ケース (B) では、 をコピーする必要がありmove
ます。std::function
std::function
そのストアにより、値渡しがより最適になります。 のコピーを格納している可能性がある場合は、std::function
値渡しします。それ以外の場合は、どちらの方法もほぼ同等です。値渡しの唯一の欠点は、同じかさばる を取得しstd::function
、サブメソッドが次々に使用する場合です。それ以外の場合は、 はmove
と同じくらい効率的ですconst&
。
さて、 内に永続的な状態がある場合に主に発生する、2 つの違いは他にもいくつかありますstd::function
。
std::function
が を持つオブジェクトを格納しているoperator() const
が、変更するデータ メンバーもいくつかあると仮定しますmutable
(失礼ですね)。
このstd::function<> const&
場合、mutable
変更されたデータ メンバーは関数呼び出しの外に伝播します。 このstd::function<>
場合、伝播しません。
これは比較的奇妙なコーナーケースです。
std::function
他の重量があり、安価に移動可能なタイプと同じように扱う必要があります。移動は安価ですが、コピーは高価になる可能性があります。