1. std::bind を説明する前に、まず std::function を確認しましょう。
std::function は「呼び出し可能オブジェクト」ラッパーであり、クラス メンバー関数ポインターを除くすべての呼び出し可能オブジェクトに対応できるクラス テンプレートです。関数、関数オブジェクト、関数ポインターを処理でき、それらの実行の保存と遅延が可能です。
呼び出し可能なオブジェクトとは何ですか?
(1) 関数ポインタ
(2) Operator() メンバー関数 (レジェンダリー ファンクター)、ラムダ式を持つ
クラス オブジェクト (3) 関数ポインタに変換できるクラス オブジェクト
(4) クラス メンバー (関数) ポインタ
( 5) バインド式または他の関数オブジェクト
例えば:
// 普通函数
int add(int a, int b){return a+b;}
// lambda表达式
auto mod = [](int a, int b){ return a % b;}
// 函数对象类
struct divide{
int operator()(int denominator, int divisor){
return denominator/divisor;
}
};
上記の型を std::function で保存します。
std::function<int(int ,int)> a = add;
std::function<int(int ,int)> b = mod ;
std::function<int(int ,int)> c = divide();
2、std::bind
std::bind 関数は、「呼び出し可能オブジェクト」を受け取り、元のオブジェクトの引数リストに「適合する」新しい呼び出し可能オブジェクトを生成する汎用関数アダプターと考えることができます。
std::bind には主に次の 2 つの機能があります:
(1) 呼び出し可能オブジェクトとそのパラメーターをアンチ関数にバインドする;
(2) 一部のパラメーターのみをバインドし、呼び出し可能オブジェクトによって渡されるパラメーターを削減します。
1. 共通関数のバインディング
double divide (double x, double y) {
return x/y;
}
auto divide_half = std::bind (my_divide,_1,2);
std::cout << fn_half(10) << std::endl; // 输出5
(1)バインドの第一引数は関数名であり、実引数として通常の関数を使用した場合、暗黙的に関数ポインタに変換されます。したがって、 std::bind (divide,_1,2) は std::bind (÷,_1,2); と同等です
(2) _1 は、<function> にあるプレースホルダーを表します、std::placeholders::_1;
2. メンバー関数のバインディング
#include <functional>
#include <iostream>
using namespace std;
struct Foo {
void sum(int n1, int n2)
{
std::cout << n1+n2 << std::endl;
}
static int StaticMember(int a) {
return 66 + a;
}
int data = 10;
};
int main() {
Foo foo;
auto f = std::bind(&Foo::sum, &foo, 95, std::placeholders::_1);
f(5); // 输出100
}
(1)bind がクラスのメンバー関数をバインドする場合、最初のパラメーターはオブジェクトのメンバー関数のポインターを表し、2 番目のパラメーターはオブジェクトのアドレスを表します。
(2) コンパイラはオブジェクトのメンバ関数を暗黙的に関数ポインタに変換しないため、明示的に &Foo::sum を指定する必要があるため、Foo::sum; の前に & を追加する必要があります (3) オブジェクトのポインタを使用する場合member
関数では、ポインタがどのオブジェクトに属しているかを知る必要があるため、2 番目のパラメータはオブジェクトのアドレスです &foo;
3. 静的メンバー関数のバインド
auto fun2 = &Foo::StaticMember;
cout<<fun2(3)<<endl;
4. メンバー変数のバインド
#include <functional>
#include <iostream>
using namespace std;
class TestClass4 {
public:
TestClass4() : m_a(100)
{}
public:
int m_a;
};
int main() {
TestClass4 test4;
auto fun4 = std::bind(&TestClass4::m_a, std::placeholders::_1);
int var = fun4(test4);
std::cout<<var<<std::endl;
}
5. バインディングファンクター
#include <functional>
#include <iostream>
using namespace std;
class TestClass5 {
public:
TestClass5() = default;
TestClass5(const TestClass5& obj) {
std::cout<<"TestClass5 copy construct."<<std::endl;
}
void operator()(int a) {
std::cout<<a<<std::endl;
}
};
int main(){
TestClass5 test5;
auto fun5 = test5;
fun5(2018);
}
6. メンバー関数をバインドするとき、最初に「コピー コンストラクター」を呼び出すメソッドはどれですか?
#include <functional>
#include <iostream>
using namespace std;
class TestClass {
public:
TestClass(int a):m_a(a){}
TestClass(const TestClass& obj) {
m_a = obj.m_a + 100;
std::cout<<"copy construct."<<std::endl;
}
int ClassMember(int a) {
std::cout<<" this:"<<this<<" :"<<&m_a<<" "<<m_a<<std::endl;
return 55 + a;
}
int ClassMember2(int a,char ch,float f) {
std::cout <<ch <<" "<< f << " "<<a<<std::endl;
return 55 + a;
}
static int StaticMember(int a) {
return 66 + a;
}
public:
int m_a;
};;
int main(){
TestClass test(67);
std::cout<<"&test "<<&test<<" "<<test.m_a<<" &test.m_a "<<&test.m_a<<std::endl;
std::cout<<"---------------------------------------------------------------------"<<std::endl;
auto fun4 = std::bind(&TestClass::ClassMember, test, std::placeholders::_1); // 调用拷贝构造函数
fun4(4);
std::cout<<"---------------------------------------------------------------------"<<std::endl;
auto fun5 = std::bind(&TestClass::ClassMember, &test, std::placeholders::_1); // 不调用拷贝构造函数
fun5(5);
std::cout<<"---------------------------------------------------------------------"<<std::endl;
auto fun6 = &TestClass::StaticMember; // 不调用拷贝构造函数
std::cout<<fun6(6)<<std::endl;
std::cout<<"---------------------------------------------------------------------"<<std::endl;
auto fun7 = std::bind(&TestClass::m_a, std::placeholders::_1); // 不调用拷贝构造函数
std::cout<<fun7(test)<<" "<<test.m_a<<std::endl;
}
結果:
7. 参照パラメータの受け渡しの問題
バインドでオブジェクトをパラメータに渡す必要がある場合は、ref または cref を使用する必要があります。
#include<iostream>
#include<functional>
using namespace std;
using namespace placeholders;
void alter(int &a, int b) {
a = b;
}
int main() {
int a = 5;
auto f1 = bind(alter, a, _1); //此处bind中的参数a只是a的一个拷贝,而不是a的引用
f1(9);
cout << a << endl; //所以此处a仍为5
auto f2 = bind(alter, ref(a), _1); //使用ref可以将a的引用传递给bind
f2(9);
cout << a << endl; //所以此处a变成了9
return 0;
}
8. メンバ関数へのポインタ
#include <iostream>
struct Foo {
int value;
void f1() { std::cout << "f1(" << this->value << ")\n"; }
void f2() { std::cout << "f2(" << this->value << ")\n"; }
};
void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}
int main() {
Foo foo1{1};
Foo foo2{2};
apply(&foo1, &foo2, &Foo::f1);
apply(&foo1, &foo2, &Foo::f2);
}
(1) メンバ関数ポインタの定義: void (Foo::*fun)()、呼び出し時に渡される実パラメータ: &Foo::f;
(2) fun はクラスのメンバ関数ポインタであるため、メンバ関数 *fun は呼び出し時に逆参照して取得する必要があります (foo1->*fun)()。
参考:
(1) C++11のstd::functionとstd::bind
(2) c++bind関数の使用