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
ことによって、関数ラッパーを作成しますfunc1
。f
これは関数ポインターであるため、その型は と一致し、 type のstd::function<int(int, int)>
2 つの引数を受け取り、 type の値を返します。ラップされた関数を呼び出してパラメータを渡すことができます。int
int
func1
2. 関数オブジェクト
// 函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;
Functor
この例では、関数オブジェクトのインスタンスを作成し、それをstd::function
関数のコンストラクターに渡して関数ラッパーを作成しますfunc2
。Functor
これは呼び出し可能なオブジェクト (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
へのポインターを取得し、それをコンストラクターに渡して関数ラッパーを作成します。静的メンバー関数はクラスのインスタンスに依存しないため、追加のオブジェクト パラメーターは必要ありません。ラップされた静的メンバー関数を呼び出してパラメーターを渡すことができます。Plus
plusi
std::function
func4
func4
5. クラスの通常のメンバー関数
//类的普通成员函数
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;
この例では、 を使用して関数ラッパーを作成し、クラスの通常のメンバー関数へのポインター&Plus::plusd
を取得し、それをクラスのコンストラクターに渡します。通常のメンバー関数はクラスのインスタンスに依存するため、追加のクラス オブジェクトをパラメータとして渡す必要があります。ラップされた通常のメンバー関数を呼び出して、クラス オブジェクトやその他のパラメーターを渡すことができます。Plus
plusd
std::function
func5
func5
ラッパーを使用して、テンプレートの効率の低さや複数のコピーのインスタンス化の問題をどのように解決すればよいでしょうか?
#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
。
ラッパーのその他のシナリオ:
ラッパーの以前の解決策を使用すると、次のようになります。
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
。を呼び出すとnewCallable
、newCallable
それが呼び出され、その中のパラメータ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
パラメータにバインドし10
、20
新しい関数オブジェクトを作成しますboundFunc
。Call は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
およびパラメーターとバインドすると10
、20
新しい関数オブジェクトが作成されますboundFunc
。Call は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
と、新しい関数オブジェクトが作成されますboundFunc
。Call はboundFunc(10)
元のメンバー関数を呼び出しprintSum
、それを最初の引数として渡し10
、バインドされた定数を20
2 番目の引数として渡します。出力は「合計: 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::_1
std::placeholders::_2
boundFunc
boundFunc(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
}