目次
4. std::bind と std::function を一緒に使用する
VC++ の共通機能開発の概要 (コラム記事のリスト、購読歓迎、継続的な更新...) https://blog.csdn.net/chenlycly/article/details/124272585 C++ ソフトウェア異常トラブルシューティング チュートリアル シリーズ入門から習熟まで(コラム記事一覧)、ぜひ購読して更新を続けてください...)https://blog.csdn.net/chenlycly/article/details/125529931入門から習熟までのC++ソフトウェア解析ツール事例集(コラム)記事は更新中...) https://blog.csdn.net/chenlycly/article/details/131405795 C/C++ の基礎と応用 (コラム記事、継続的に更新中...) https://blog.csdn.net /chenlycly/category_11931267.html C++ 11 の新機能は非常に重要です。C++ 開発者として、学習する必要があります。筆記試験の面接に含まれるだけでなく、オープン ソース コードで大規模に使用されることになります。多くのビデオ会議やライブ ブロードキャスト ソフトウェアで使用されているオープン ソースの WebRTC プロジェクトを例に挙げます。WebRTC コードは C++11 以降の新機能を広範囲に使用しています。そのソース コードを理解するには、C++ のこれらの新機能を理解する必要があります。したがって、次の期間では、参考または参照のために、私の仕事の実践を組み合わせて、C++11 の新機能について詳しく説明します。
1 はじめに
C++11 では、テンプレート クラス std::function と標準ライブラリ関数 std::bind が導入されており、これら 2 つの機能により C++ がより柔軟になります。呼び出し元のオブジェクトは、std::function クラス テンプレートを使用してラップできます。標準ライブラリ関数 std::bind を呼び出して、元の関数に基づいて新しい関数を生成し、簡単に呼び出すことができます。これら 2 つの新機能について、以下で詳しく説明します。
2. std::function 関数テンプレート クラス
C++ では、呼び出し可能なエンティティには主に、関数、関数ポインター、関数参照、暗黙的に関数仕様に変換できるオブジェクト、または opetator() を実装するオブジェクトが含まれます。
C++11 では、新しい std::function クラス テンプレートが追加されます。これは、C++ の既存の呼び出し可能なエンティティのタイプ セーフ ラッパーです。std::function は、関数、ラムダ式、バインド式、その他の関数オブジェクトなどの呼び出し可能なターゲットのインスタンスに加え、メンバー関数へのポインターやデータ メンバーへのポインターを格納、コピー、および呼び出すことができます。テンプレートパラメータを指定することで、関数、関数オブジェクト、関数ポインタを統一的に扱い、保存したり遅延したりすることができます。
このテンプレート クラスに格納されている呼び出し可能オブジェクトは、ターゲットの std::function と呼ばれます。std::function インスタンスにターゲットが含まれていない場合、それは空であると言われます。空の std::function オブジェクトを呼び出すと、例外 std::bad_function_call がスローされます。
#include <iostream>
#include <functional> //std::cout
using namespace std;
void func(void)
{//普通全局函数
cout << __func__ << endl;
}
class Foo
{
public:
static int foo_func(int a)
{//类中静态函数
cout << __func__ << "(" << a << ") ->: ";
return a;
}
};
class Bar
{
public:
int operator()(int a)
{//仿函数
cout << __func__ << "(" << a << ") ->: ";
return a;
}
};
int main()
{
//绑定一个普通函数
function< void(void) > f1 = func;
f1();
//绑定类中的静态函数
function< int(int) > f2 = Foo::foo_func;
cout << f2(111) << endl;
//绑定一个仿函数
Bar obj;
f2 = obj;
cout << f2(222) << endl;
/*
运行结果:
func
foo_func(111) ->: 111
operator()(222) ->: 222
*/
return 0;
}
std::function オブジェクトの最大の用途は関数コールバックの実装です。ユーザーは、これを等価性または不等号性のチェックには使用できませんが、NULL または nullptr との比較は可能であることに注意する必要があります。
3.std::bind関数
std::bind は、指定された呼び出し可能エンティティの一部のパラメーターを既存の変数に事前バインドして、新しい呼び出し可能エンティティを生成できるメカニズムです。このメカニズムは、コールバック関数の使用にも非常に役立ちます。
C++98 では、ファンクターの 1 番目と 2 番目のパラメーターをそれぞれバインドするために使用できる関数 binding1st と binding2nd の 2 つがありますが、バインドできるパラメーターは 1 つだけであり、さまざまな制限により、bind1st と binding2nd の可用性が大幅に低下します。
C++11 では std::bind が提供されていますが、バインドするパラメータの数や特定のパラメータに制限はなく、ユーザーが指定します。このバインドが本当のバインディングです。バインド関数は、呼び出し可能オブジェクトを受け取り、元のオブジェクトのパラメーター リストに「適応」する新しい呼び出し可能オブジェクトを生成する汎用関数アダプターと考えることができます。バインド呼び出しの一般的な形式は次のとおりです。
auto newCallable = bind( callable, arg_lsit);
このうち、newCallable は呼び出し可能なオブジェクトであり、arg_lsit は呼び出し可能な呼び出し可能オブジェクトに渡されるパラメータに対応するカンマ区切りのパラメータ リストです。new newCallable を呼び出すと、newCallable は callable を呼び出し、arg_lsit のパラメータを callable に渡します。
さらに、arg_lsit の引数には、_n (n は整数) という形式の名前が含まれる場合があります。そのような引数は、newCallable に渡される n 番目の引数の「プレースホルダー」です。_n は、std 空間にあるプレースホルダーと呼ばれる名前空間で定義されているため、_1 に対応する using ステートメントは次のようになります。
using std::placeholders::_1。
バインド関数を使用してラムダ式を置き換える例を次に示します。たとえば、文字列オブジェクトを格納するベクトル リストがあります。
std::vector<string> strList;
ラムダ式を使用して、リスト内の文字列長が 6 以上の要素を検索します。
DWORD dwLen = 6;
find_if( strList.begin(), strList.end(), [dwLen](const string & str )
{ str.size() >= dwLen;}); // 使用lambda表达式
ラムダ関数を使用して実装するのは比較的簡単です。
通常の関数を使用して文字列の長さを比較したい場合、find_if を呼び出すにはどうすればよいでしょうか? たとえば、文字列の長さを検出する関数は次のとおりです。
bool CheckStrLen( const string& str, DWORD dwLen )
{
return str.size() >= dwLen;
}
find_if に渡される比較関数にはパラメータが 1 つしかなく、ここでの比較関数にはパラメータが 2 つあるため、直接渡すことはできません。バインド関数を使用して新しい関数を構築できます。
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen ); // 调用CheckStrLenBind,就会调用CheckStrLen,然后给CheckStrLen传递两个参数
ここでは文字列を例にして、bind 関数を呼び出すプロセスを確認します。
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen );
string str = “hello”;
bool bRet = CheckStrLenBind( str ); // 此处相当于调用CheckStrLen(str, dwLen)
したがって、find_if を呼び出すときは次のように記述できます。
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen );
find_if( strList.begin(), strList.end(), CheckStrLenBind);
find_if は文字列クラス オブジェクトを CheckStrLenBind に渡し、次に CheckStrLenBind はこの文字列クラス オブジェクトを最初のパラメータとして最終関数 CheckStrLen に渡し、同時に dwLen を CheckStrLen に渡します。
次のように直接記述することもできます。
find_if( strList.begin(), strList.end(), bind( CheckStrLen, _1, dwLen ));
4. std::bind と std::function を一緒に使用する
std::bind と std::function を一緒に使用して、すべての呼び出し可能なオブジェクトが統一された操作メソッドを持つようにすることもできます。以下の例を参照してください。
#include <iostream>
#include <functional> //std::cout
using namespace std;
using namespace std::placeholders; // adds visibility of _1, _2, _3,...
class Test
{
public:
int i = 0;
void func(int x, int y)
{
cout << x << " " << y << endl;
}
};
int main()
{
Test obj; //创建对象
function<void(int, int)> f1 = bind(&Test::func, &obj, _1, _2);
f1(1, 2); //输出:1 2
function< int &()> f2 = bind(&Test::i, &obj);
f2() = 123;
cout << obj.i << endl;//结果为 123
return 0;
}