<C++> C++11 ラッパー関数とバインド

1. 関数ラッパー

C++11 では、std::function関数ラッパーは次を使用して作成できます。std::functionこれは、関数ポインター、関数オブジェクト、Lambda 式などを含むさまざまな呼び出し可能なオブジェクトを保存、コピー、呼び出しできる一般的な関数オブジェクトです。functionラッパーはアダプターとも呼ばれます。function本質はクラス テンプレートとラッパーです。

ret = func(x);

そこにはfunc何があるでしょうか?それでfunc関数名でしょうか?関数ポインタ? 関数オブジェクト (ファンクター オブジェクト)? また、おそらくラムダ式オブジェクトでしょうか? つまり、これらは呼び出し可能な型です。このような豊富な型は非効率的なテンプレートにつながる可能性があります。なぜ?続きを読みましょう

template<class F, class T>
T useF(F f, T x) {
    
    
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    
    
    return i / 2;
}

struct Functor {
    
    
    double operator()(double d) {
    
    
        return d / 3;
    }
};

int main() {
    
    // 函数名
    cout << useF(f, 11.11) << endl;
    // 函数对象
    cout << useF(Functor(), 11.11) << endl;
    // lamber表达式
    cout << useF([](double d) -> double {
    
     return d / 4; }, 11.11) << endl;
    return 0;
}

出力は次のとおりです。

count:1
count:00EDC140
5.555
count:1
count:00EDC144
3.70333
count:1
count:00EDC148
2.7775

3 つのカウントのアドレスが異なることがわかり、useF関数テンプレートが 3 回インスタンス化されたことがわかります。

機能の使い方

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
#include <functional>
int f(int a, int b){
    
    
	return a + b;
}

struct Functor{
    
    
public:
	int operator() (int a, int b){
    
    
		return a + b;
	}
};

class Plus{
    
    
public:
	static int plusi(int a, int b){
    
    
		return a + b;
	}
	double plusd(double a, double b){
    
    
		return a + b;
	}
};

1. 関数ポインタ

// 函数名(函数指针)
std::function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;

この例では、f関数ポインターを のコンストラクターに渡すstd::functionことによって、関数ラッパーを作成しますfunc1fこれは関数ポインターであるため、その型は と一致し、 type のstd::function<int(int, int)>2 つの引数を受け取り、 type の値を返します。ラップされた関数を呼び出してパラメータを渡すことができます。intintfunc1

2. 関数オブジェクト

// 函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;

Functorこの例では、関数オブジェクトのインスタンスを作成し、それをstd::function関数のコンストラクターに渡して関数ラッパーを作成しますfunc2Functorこれは呼び出し可能なオブジェクト (operator()関数を定義)であるため、その型はstd::function<int(int, int)>と一致します。func2ラップされた関数オブジェクトを呼び出してパラメータを渡すことができます。

3. ラムダ式

// Lambda表达式
std::function<int(int, int)> func3 = [](const int a, const int b) {
    
     return a + b; };
cout << func3(1, 2) << endl;

この例では、Lambda 式を使用して匿名関数を作成し、それをstd::function関数のコンストラクターに渡して関数ラッパーを作成しましたfunc3ラムダ式の型はコンパイラによって推定され、 とstd::function<int(int, int)>一致します。func3ラップされた Lambda 関数を呼び出してパラメータを渡すことができます。

4. クラスの静的メンバー関数

// 类的静态成员函数
std::function<int(int, int)> func4 = &Plus::plusi;
cout << func4(1, 2) << endl;

この例では、 を使用してクラスの静的メンバー関数&Plus::plusiへのポインターを取得し、それをコンストラクターに渡して関数ラッパーを作成します静的メンバー関数はクラスのインスタンスに依存しないため、追加のオブジェクト パラメーターは必要ありません。ラップされた静的メンバー関数を呼び出してパラメーターを渡すことができます。Plusplusistd::functionfunc4func4

5. クラスの通常のメンバー関数

//类的普通成员函数
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;

この例では、 を使用して関数ラッパーを作成し、クラスの通常のメンバー関数へのポインター&Plus::plusdを取得し、それをクラスのコンストラクターに渡します通常のメンバー関数はクラスのインスタンスに依存するため、追加のクラス オブジェクトをパラメータとして渡す必要がありますラップされた通常のメンバー関数を呼び出して、クラス オブジェクトやその他のパラメーターを渡すことができます。Plusplusdstd::functionfunc5func5

ラッパーを使用して、テンプレートの効率の低さや複数のコピーのインスタンス化の問題をどのように解決すればよいでしょうか?

#include <functional>
#include <iostream>
using namespace std;

template<class F, class T>
T useF(F f, T x) {
    
    
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    
    
    return i / 2;
}

struct Functor {
    
    
    double operator()(double d) {
    
    
        return d / 3;
    }
};

int main() {
    
    // 函数名
    std::function<double(double)> func1 = f;
    cout << useF(func1, 11.11) << endl;
    // 函数对象
    std::function<double(double)> func2 = Functor();
    cout << useF(func2, 11.11) << endl;
    // Lambda表达式
    std::function<double(double)> func3 = [](double d) -> double {
    
     return d / 4; };
    cout << useF(func3, 11.11) << endl;
    return 0;
}

出力結果:

count:1
count:0071E4C8
5.555
count:2
count:0071E4C8
3.70333
count:3
count:0071E4C8
2.7775

その結果、3 つのカウントアドレスは同じであり、1 つのコピーのみがインスタンス化されていることがわかりますfunction

ラッパーのその他のシナリオ:

150. 逆ポーランド語式の評価 - LeetCode

ラッパーの以前の解決策を使用すると、次のようになります。

class Solution {
    
    
public:
    int evalRPN(vector<string> &tokens) {
    
    
        stack<int> st;
        for (auto &str: tokens) {
    
    
            if (str == "+" || str == "-" || str == "*" || str == "/") {
    
    
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch (str[0]) {
    
    
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                }
            } else {
    
    
                // 1、atoi itoa
                // 2、sprintf scanf
                // 3、stoi to_string C++11
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

ラッパーを使用した解決策:

class Solution {
    
    
public:
    int evalRPN(vector<string> &tokens) {
    
    
        stack<long> st;
        unordered_map<string, function<long(long, long)>> opFuncMap = {
    
    
                {
    
    "+", [](long x, long y) -> long {
    
     return x + y; }},
                {
    
    "-", [](long x, long y) -> long {
    
     return x - y; }},
                {
    
    "*", [](long x, long y) -> long {
    
     return x * y; }},
                {
    
    "/", [](long x, long y) -> long {
    
     return x / y; }}};

        for (auto &str: tokens) {
    
    
            if (opFuncMap.find(str) != opFuncMap.end()) {
    
    
                long right = st.top();
                st.pop();
                long left = st.top();
                st.pop();

                st.push(opFuncMap[str](left, right));
            } else {
    
    
                st.push(stoll(str));
            }
        }

        return st.top();
    }
};

2.バインド

std::bindは、C++ 標準ライブラリの関数テンプレートであり、<functional>ヘッダー ファイルにあります。これは関数オブジェクト (関数バインダーとも呼ばれます) を作成するために使用され、呼び出し可能オブジェクトをパラメーターとバインドして新しい呼び出し可能オブジェクトを形成できます。

プロトタイプは次のとおりです。

template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

bind関数は、呼び出し可能オブジェクトを受け取り、元のオブジェクトの引数リストに「適合する」新しい呼び出し可能オブジェクトを生成する汎用関数アダプターと考えてくださいバインド呼び出しの一般的な形式は次のとおりです。auto newCallable = bind(callable,arg_list);

ここでnewCallable、それ自体は呼び出し可能なオブジェクトであり、arg_list指定された引数に対応する引数のカンマ区切りのリストですcallableを呼び出すとnewCallablenewCallableそれが呼び出され、その中のパラメータcallableに渡されます。arg_list

arg_listのパラメータには、n という形式の名前が含まれる場合があります。n は整数であり、これらのパラメータは「プレースホルダ」であり、に渡されるパラメータの「位置」をnewCallable占めるパラメータを示します。newCallable値 n は、結果の呼び出し可能オブジェクト内のパラメーターの位置を示します。最初newCallableのパラメーターの場合は 1、2 番目のパラメーターの場合は _2 などとなります。

1. バインディング関数ポインタ

#include <functional>
#include <iostream>

void foo(int a, int b) {
    
    
    std::cout << "Sum: " << (a + b) << std::endl;
}

int main() {
    
    
    auto boundFunc = std::bind(foo, 10, 20);
    boundFunc(); // 调用绑定的函数指针
    return 0;
}

std::bind関数ポインタをfooパラメータにバインドし1020新しい関数オブジェクトを作成しますboundFuncCall はboundFunc()元の関数を呼び出しfoo、バインドされたパラメータを渡します。出力は「合計: 30」です。

2. メンバー関数のバインディング

#include <functional>
#include <iostream>

class MyClass {
    
    
public:
    void printSum(int a, int b) {
    
    
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    
    
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, 10, 20);
    boundFunc(); // 调用绑定的成员函数
    return 0;
}

std::bindメンバー関数をprintSumオブジェクト ポインター&objおよびパラメーターとバインドすると1020新しい関数オブジェクトが作成されますboundFuncCall はboundFunc()元のメンバー関数を呼び出しprintSum、バインドされたオブジェクトとパラメーターを渡します。出力は「合計: 30」です。

3. バインディングメンバー関数とプレースホルダー

#include <functional>
#include <iostream>

class MyClass {
    
    
public:
    void printSum(int a, int b) {
    
    
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    
    
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, std::placeholders::_1, 20);
    boundFunc(10); // 调用绑定的成员函数,传递部分参数
    return 0;
}

std::bindメンバー関数printSumをオブジェクト ポインター&obj、プレースホルダーstd::placeholders::_1(最初のパラメーター用)、および定数とバインドする20と、新しい関数オブジェクトが作成されますboundFuncCall はboundFunc(10)元のメンバー関数を呼び出しprintSum、それを最初の引数として渡し10、バインドされた定数を202 番目の引数として渡します。出力は「合計: 30」です。

4. パラメータの再配置とデフォルトパラメータ

#include <functional>
#include <iostream>

void foo(int a, int b, int c) {
    
    
    std::cout << "Sum: " << (a + b * c) << std::endl;   //230
}

int main() {
    
    
    auto boundFunc = std::bind(foo, std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
    boundFunc(10, 20, 30); // 调用绑定的函数,重排参数顺序    
    return 0;
}

std::bind関数をfooプレースホルダーとバインドするstd::placeholders::_3新しい関数オブジェクトが作成されますこの呼び出しでは元の関数 が呼び出され、パラメータは並べ替えられた順序で渡され、a は 3 番目のパラメータ 30 にバインドされ、b は最初のパラメータ 10 にバインドされ、c は 2 番目のパラメータ 20 にバインドされます。つまり、30+10*20 = 230 となります。std::placeholders::_1std::placeholders::_2boundFuncboundFunc(10, 20, 30)foo

std::bindまた、固定定数がデフォルト パラメータとして使用される、バインドされたパラメータのデフォルト設定もサポートしstd::bind(foo, _1, _2, 100)ます100

関数を使用して結合する

使用例

#include <functional>
#include <iostream>
#include <map>
#include <string>
using namespace std;
using namespace placeholders;

int Plus(int a, int b) {
    
    
    return a + b;
}

int Mul(int a, int b, int c, double rate) {
    
    
    return a + b + c * rate;
}

class Sub {
    
    
public:
    int sub(int a, int b) {
    
    
        return a - b;
    }
};

int main() {
    
    
    // 调整顺序 -- 鸡肋
    // _1 _2.... 定义在placeholders命名空间中,代表绑定函数对象的形参,
    // _1,_2...分别代表第一个形参、第二个形参...
    function<int(int, int)> funcPlus = Plus;
    //function<int(Sub, int, int)> funcSub = &Sub::sub;
    function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _2, _1);
    function<int(int, int, int)> funcMul = bind(Mul, _3, _1, _2, 1.5);

    cout << funcPlus(1, 2) << endl;  //3
    cout << funcSub(1, 2) << endl;   //1
    cout << funcMul(3, 2, 1) << endl;//a = 1, b = 3 c = 2 * 1.5   1+3+2*1.5=7
 
    return 0;
}
int main() {
    
    
    map<string, function<int(int, int)>> opFuncMap = {
    
    
            {
    
    "+", Plus},
            {
    
    "-", bind(&Sub::sub, Sub(), _1, _2)},
            {
    
    "*", bind(Mul, _2, _1, 1, 2)},//这里只能有两个占位符,function的参数只有2个
    };


    cout << opFuncMap["+"](1, 2) << endl;//3
    cout << opFuncMap["-"](1, 2) << endl;//-1
    cout << opFuncMap["*"](2, 3) << endl;//2+3+1*2=7
}

おすすめ

転載: blog.csdn.net/ikun66666/article/details/131367399