std::function
を取る関数があるとしましょう:
void callFunction(std::function<void()> x)
{
x();
}
代わりにx
をconst-referenceで渡す必要がありますか?:
void callFunction(const std::function<void()>& x)
{
x();
}
この質問に対する答えは、関数がそれをどうするかによって変わりますか?たとえば、std::function
をメンバー変数に格納または初期化するクラスメンバー関数またはコンストラクターである場合。
パフォーマンスが必要な場合は、保存する場合は値で渡します。
「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
dからstd::function
に変換されるため、移動可能な状態はすべて効率的に保持されます。
2番目の場合、一時的なstd::function
が作成され、ラムダはそれにmove
dされ、その一時的なstd::function
はrun_in_ui_thread
内で参照によって使用されます。
これまでのところ、非常に良い-それらの2つは同じように機能します。 run_in_ui_thread
がその関数引数のコピーを作成して、実行するためにUIスレッドに送信する場合を除きます! (処理が完了する前に戻るため、参照を使用することはできません)。ケース(A)の場合は、move
をstd::function
だけで長期保存します。 (B)の場合、std::function
のコピーを強制されます。
そのストアは、値渡しをより最適にします。 std::function
のコピーを保存している可能性がある場合は、値で渡します。それ以外の場合、どちらの方法もほぼ同等です。値によるマイナス面は、同じかさばるstd::function
を使用し、サブメソッドを次々に使用する場合のみです。それを除いて、move
はconst&
と同じくらい効率的です。
ここで、std::function
内に永続的な状態がある場合、主に起動する2つの間にいくつかの違いがあります。
std::function
にはoperator() const
を持つオブジェクトが格納されますが、変更するmutable
データメンバもあると仮定します(失礼です!)。
std::function<> const&
の場合、変更されたmutable
データメンバーは、関数呼び出しから伝達されます。 std::function<>
の場合、そうではありません。
これは比較的奇妙なコーナーケースです。
std::function
を、他のおそらく重量があり、安価に移動できるタイプと同じように扱います。移動は安価で、コピーには費用がかかります。
パフォーマンスが心配で、仮想メンバー関数を定義していない場合は、std::function
を使用しないでください。
ファンクタータイプをテンプレートパラメーターにすると、ファンクターロジックのインライン化など、std::function
よりも大きな最適化が可能になります。これらの最適化の効果は、std::function
を渡す方法に関するcopy-vs-indirectionの懸念を大きく上回る可能性があります。
もっと早く:
template<typename Functor>
void callFunction(Functor&& x)
{
x();
}
C++ 11での通常のように、value/reference/const-referenceによる受け渡しは、引数をどうするかによって異なります。 std::function
も同様です。
値渡しを使用すると、引数を変数(通常はクラスのメンバー変数)に移動できます。
struct Foo {
Foo(Object o) : m_o(std::move(o)) {}
Object m_o;
};
関数が引数を移動することがわかっている場合、これが最良の解決策です。これにより、ユーザーは関数の呼び出し方法を制御できます。
Foo f1{Object()}; // move the temporary, followed by a move in the constructor
Foo f2{some_object}; // copy the object, followed by a move in the constructor
Foo f3{std::move(some_object)}; // move the object, followed by a move in the constructor
(非)const-referencesのセマンティクスを既に知っていると思いますので、その点については説明しません。これについての説明を追加する必要がある場合は、お問い合わせください。更新します。