私が探しているコードは次のようなものです。
bool Func1(int Arg1, C++11LambdaFunc Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
}
後でこのコードを使用します。
Func1(12, [](int D) -> bool { ... } );
ヘッダーファイルで使用する基本バージョン:
_template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
_
より複雑なバージョン、インターフェースを実装から分離したい場合(実行時間のコストがかかります):
_bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
_
_std::function
_は、型消去を使用してラムダのカスタム作成ラッパーを作成し、pImpl
パターンを使用する非仮想インターフェイスを公開して、カスタム作成ラッパーに転送します。1
または、あまり技術的には、std::function<bool(int)>
は、関数のように呼び出すことができるほぼすべてをラップできるクラスであり、int
を渡すことと互換性のある1つのパラメーターを渡します。 bool
。
_std::function
_を介した呼び出しは、virtual
関数呼び出し(上記のタイプの消去によって引き起こされる)とほぼ等しい実行時コストを持ち、作成するときは、渡された関数オブジェクト(別名ファンクター)の状態をコピーする必要がありますで(これは安価である可能性があります-ステートレスラムダ、または参照によって引数をキャプチャするラムダ-または他の場合には高価です)、それを保存します(通常は無料ストアまたはヒープに費用がかかります)呼び出しの時点で「インライン化」できます(つまり、関数呼び出しよりもコストが低いだけでなく、コンパイラーは関数呼び出しを最適化して境界を返すことさえできます!)
最初の例の派手なバージョンで、いくつかのコーナーケースも少しうまく処理します:(ヘッダーファイル内、または使用されているのと同じ翻訳単位でも実装する必要があります)
_template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
if(Arg1 > 0){
return std::forward<Lambda>(Arg2)(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
_
「完全転送」として知られる手法を使用します。一部のファンクターの場合、これは#1とは少し異なる動作を生成します(通常はより正確な動作)。
改善のほとんどは、引数リストで_&&
_を使用することによってもたらされます。これは、ファンクターへの参照が(コピーの代わりに)渡されることを意味し、コストを節約し、const
またはnonconst
渡されるファンクター。
std::forward<Lambda>(...)
の変更は、誰かがメソッド(operator()
を含む)がthis
ポインターの右辺値/左辺値ステータスをオーバーライドできる比較的新しいC++機能を使用した場合にのみ動作の変更を引き起こします。理論的には、これは有用かもしれませんが、実際にthis
の右辺値ステータスに基づいてオーバーライドするファンクターの数は_0
_です。深刻なライブラリコード(tm)を書いているとき、私はこのわざわざに行きますが、そうでないことはめったにありません。
考慮すべきことがもう1つあります。 bool
を返す関数、またはvoid
を返す関数のいずれかを使用し、関数がvoid
を返す場合、true
を返したものとして処理する必要があるとします。例として、コレクションを反復処理するときに呼び出される関数を使用しており、オプションで早期停止をサポートしたい場合があります。この関数は、途中で停止したい場合はfalse
を返し、そうでない場合はtrue
またはvoid
を返します。
または、より一般的なケースでは、関数のオーバーライドが複数あり、そのうちの1つが関数を受け取り、他の関数が同じ場所で他のタイプを受け取ります。
これは可能です。私がここに入るまでです(スマートアダプターを使用するか、SFINAEテクニックを使用して)。ただし、2つの異なる名前付き関数を作成することをお勧めします。これは、必要な手法が非常に重いためです。
1 技術的には_std::function
_は、その動作が実装ではなく標準によって記述されているため、魔法の妖精の塵を使用してそれを行うことができます。私がやり取りした_std::function
_実装の動作を近似する単純な実装を説明しています。
最初の解決策:
Func1()
関数を関数templateにすることができます:
_template<typename T>
bool Func1(int Arg1, T&& Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false; // <== DO NOT FORGET A return STATEMENT IN A VALUE-RETURNING
// FUNCTION, OR YOU WILL GET UNDEFINED BEHAVIOR IF FLOWING
// OFF THE END OF THE FUNCTION WITHOUT RETURNING ANYTHING
}
_
その後、必要に応じて呼び出すことができます。
_int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
_
第二の解決策:
テンプレートを使用したくない場合、代替手段(実行時のオーバーヘッドをもたらす)は _std::function
_ を使用することです。
_#include <functional>
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false;
}
_
繰り返しになりますが、これによりFunc1()
を好きな方法で呼び出すことができます:
_int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
_
好みがより伝統的な人のために、キャプチャしていないラムダは関数ポインタに変換できることに注意してください。したがって、上記の関数を次のように書くことができます。
bool Func1(int Arg1, bool (*Arg2)(int)) { ... }
そして、従来の関数and lambdasの両方で正しく動作します。