C++
std :: function:呼び出し可能な要素をラップする
サーチ…
簡単な使い方
#include <iostream>
#include <functional>
std::function<void(int , const std::string&)> myFuncObj;
void theFunc(int i, const std::string& s)
{
std::cout << s << ": " << i << std::endl;
}
int main(int argc, char *argv[])
{
myFuncObj = theFunc;
myFuncObj(10, "hello world");
}
std :: bindで使用されるstd :: function
引数を持つ関数をコールバックする必要がある状況を考えてみましょう。 std::function
std::bind
使用されるstd::function
は、以下に示すような非常に強力な設計構造を提供します。
class A
{
public:
std::function<void(int, const std::string&)> m_CbFunc = nullptr;
void foo()
{
if (m_CbFunc)
{
m_CbFunc(100, "event fired");
}
}
};
class B
{
public:
B()
{
auto aFunc = std::bind(&B::eventHandler, this, std::placeholders::_1, std::placeholders::_2);
anObjA.m_CbFunc = aFunc;
}
void eventHandler(int i, const std::string& s)
{
std::cout << s << ": " << i << std::endl;
}
void DoSomethingOnA()
{
anObjA.foo();
}
A anObjA;
};
int main(int argc, char *argv[])
{
B anObjB;
anObjB.DoSomethingOnA();
}
std :: functionをlambdaとstd :: bindで実行する
#include <iostream>
#include <functional>
using std::placeholders::_1; // to be used in std::bind example
int stdf_foobar (int x, std::function<int(int)> moo)
{
return x + moo(x); // std::function moo called
}
int foo (int x) { return 2+x; }
int foo_2 (int x, int y) { return 9*x + y; }
int main()
{
int a = 2;
/* Function pointers */
std::cout << stdf_foobar(a, &foo) << std::endl; // 6 ( 2 + (2+2) )
// can also be: stdf_foobar(2, foo)
/* Lambda expressions */
/* An unnamed closure from a lambda expression can be
* stored in a std::function object:
*/
int capture_value = 3;
std::cout << stdf_foobar(a,
[capture_value](int param) -> int { return 7 + capture_value * param; })
<< std::endl;
// result: 15 == value + (7 * capture_value * value) == 2 + (7 + 3 * 2)
/* std::bind expressions */
/* The result of a std::bind expression can be passed.
* For example by binding parameters to a function pointer call:
*/
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
std::cout << b << std::endl;
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
std::cout << c << std::endl;
// c == 49 == 2 + ( 9*5 + 2 )
return 0;
}
`関数`オーバーヘッド
std::function
は重大なオーバーヘッドを引き起こす可能性があります。 std::function
は[value semantics] [1]があるため、指定された呼び出し可能コードをコピーまたは移動する必要があります。しかし、それは任意のタイプの呼び出し可能なものを取ることができるので、頻繁にこれを行うためにメモリを動的に割り当てる必要があります。
function
実装の中には、小さなオブジェクト型(関数ポインタ、メンバポインタ、非常に小さな状態のファンクタなど)がfunction
オブジェクトに直接格納される、いわゆる「小オブジェクト最適化」があります。しかし、これは、型がnoexcept
move constructibleである場合にのみ機能します。さらに、C ++標準では、すべての実装で1つを提供する必要はありません。
次の点を考慮してください。
//Header file
using MyPredicate = std::function<bool(const MyValue &, const MyValue &)>;
void SortMyContainer(MyContainer &C, const MyPredicate &pred);
//Source file
void SortMyContainer(MyContainer &C, const MyPredicate &pred)
{
std::sort(C.begin(), C.end(), pred);
}
テンプレートパラメータはSortMyContainer
の推奨ソリューションですが、何らかの理由でこれが可能ではない、または望ましいとは考えません。 SortMyContainer
は、自身の呼び出しを超えてpred
を格納する必要はありません。しかし、与えられたファンクタが単純な大きさであれば、 pred
はメモリを十分に割り当てることができます。
function
は何かをコピー/移動する必要があるためメモリを割り当てます。 function
は与えられた呼び出し可能コードの所有権を取得します。しかし、 SortMyContainer
は呼び出し可能なものを所有する必要はありません。それはそれを参照しているだけです。ここでのfunction
使用は過剰です。効率的かもしれませんが、そうでないかもしれません。
単に呼び出し可能コードを参照する標準ライブラリ関数型はありません。だから、代わりの解決策が見つかるか、オーバーヘッドで生活することを選択する必要があります。
また、 function
は、オブジェクトのメモリ割り当てがどこから来るかを制御する有効な手段はありません。はい、それは取るコンストラクタがあるallocator
、しかし[多くの実装は、それらを正しく実装する...か、まったくありません] [2]。
allocator
を受け取るfunction
コンストラクタはもはやその型の一部ではありません。したがって、割り当てを管理する方法はありません。
function
呼び出すことは、内容を直接呼び出すよりも遅くなります。どののでfunction
インスタンスが呼び出し可能に保持することができ、介してコールfunction
、間接的でなければなりません。 function
呼び出しのオーバーヘッドは、仮想関数呼び出しのオーダーです。
他の呼び出し可能な型へのstd :: functionのバインド
/*
* This example show some ways of using std::function to call
* a) C-like function
* b) class-member function
* c) operator()
* d) lambda function
*
* Function call can be made:
* a) with right arguments
* b) argumens with different order, types and count
*/
#include <iostream>
#include <functional>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using namespace std::placeholders;
// simple function to be called
double foo_fn(int x, float y, double z)
{
double res = x + y + z;
std::cout << "foo_fn called with arguments: "
<< x << ", " << y << ", " << z
<< " result is : " << res
<< std::endl;
return res;
}
// structure with member function to call
struct foo_struct
{
// member function to call
double foo_fn(int x, float y, double z)
{
double res = x + y + z;
std::cout << "foo_struct::foo_fn called with arguments: "
<< x << ", " << y << ", " << z
<< " result is : " << res
<< std::endl;
return res;
}
// this member function has different signature - but it can be used too
// please not that argument order is changed too
double foo_fn_4(int x, double z, float y, long xx)
{
double res = x + y + z + xx;
std::cout << "foo_struct::foo_fn_4 called with arguments: "
<< x << ", " << z << ", " << y << ", " << xx
<< " result is : " << res
<< std::endl;
return res;
}
// overloaded operator() makes whole object to be callable
double operator()(int x, float y, double z)
{
double res = x + y + z;
std::cout << "foo_struct::operator() called with arguments: "
<< x << ", " << y << ", " << z
<< " result is : " << res
<< std::endl;
return res;
}
};
int main(void)
{
// typedefs
using function_type = std::function<double(int, float, double)>;
// foo_struct instance
foo_struct fs;
// here we will store all binded functions
std::vector<function_type> bindings;
// var #1 - you can use simple function
function_type var1 = foo_fn;
bindings.push_back(var1);
// var #2 - you can use member function
function_type var2 = std::bind(&foo_struct::foo_fn, fs, _1, _2, _3);
bindings.push_back(var2);
// var #3 - you can use member function with different signature
// foo_fn_4 has different count of arguments and types
function_type var3 = std::bind(&foo_struct::foo_fn_4, fs, _1, _3, _2, 0l);
bindings.push_back(var3);
// var #4 - you can use object with overloaded operator()
function_type var4 = fs;
bindings.push_back(var4);
// var #5 - you can use lambda function
function_type var5 = [](int x, float y, double z)
{
double res = x + y + z;
std::cout << "lambda called with arguments: "
<< x << ", " << y << ", " << z
<< " result is : " << res
<< std::endl;
return res;
};
bindings.push_back(var5);
std::cout << "Test stored functions with arguments: x = 1, y = 2, z = 3"
<< std::endl;
for (auto f : bindings)
f(1, 2, 3);
}
出力:
Test stored functions with arguments: x = 1, y = 2, z = 3
foo_fn called with arguments: 1, 2, 3 result is : 6
foo_struct::foo_fn called with arguments: 1, 2, 3 result is : 6
foo_struct::foo_fn_4 called with arguments: 1, 3, 2, 0 result is : 6
foo_struct::operator() called with arguments: 1, 2, 3 result is : 6
lambda called with arguments: 1, 2, 3 result is : 6
関数引数をstd :: tupleに格納する
いくつかのプログラムでは、将来のある種の関数呼び出しのために引数を格納する必要があります。
次の例は、std :: tupleに格納された引数を持つ関数を呼び出す方法を示しています。
#include <iostream>
#include <functional>
#include <tuple>
#include <iostream>
// simple function to be called
double foo_fn(int x, float y, double z)
{
double res = x + y + z;
std::cout << "foo_fn called. x = " << x << " y = " << y << " z = " << z
<< " res=" << res;
return res;
}
// helpers for tuple unrolling
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
// invocation helper
template<typename FN, typename P, int ...S>
double call_fn_internal(const FN& fn, const P& params, const seq<S...>)
{
return fn(std::get<S>(params) ...);
}
// call function with arguments stored in std::tuple
template<typename Ret, typename ...Args>
Ret call_fn(const std::function<Ret(Args...)>& fn,
const std::tuple<Args...>& params)
{
return call_fn_internal(fn, params, typename gens<sizeof...(Args)>::type());
}
int main(void)
{
// arguments
std::tuple<int, float, double> t = std::make_tuple(1, 5, 10);
// function to call
std::function<double(int, float, double)> fn = foo_fn;
// invoke a function with stored arguments
call_fn(fn, t);
}
出力:
foo_fn called. x = 1 y = 5 z = 10 res=16