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 with 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]을 가지고 있기 때문에 주어진 callable을 복사하거나 자신으로 옮겨야한다. 그러나 임의의 유형의 호출 가능 호출을 사용할 수 있으므로이 작업을 수행하기 위해 자주 동적으로 메모리를 할당해야합니다.
일부 function
구현은 소규모 유형 (함수 포인터, 멤버 포인터 또는 아주 작은 상태의 펑터)이 function
객체에 직접 저장되는 소위 "객체 최적화"를 가지고 있습니다. 그러나 이것은 심지어 유형이 noexcept
move constructible 인 경우에만 작동합니다. 게다가, C ++ 표준은 모든 구현이 하나를 제공 할 것을 요구하지 않습니다.
다음을 고려하세요:
//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
, 어떤 이유로 든 가능하지 않거나 바람직하지 않다고 가정합니다. SortMyContainer
는 자신의 호출 이상으로 pred
를 저장할 필요가 없습니다. 그리고 pred
는 주어진 functor가 사소한 크기라면 메모리를 할당 할 수 있습니다.
function
는 복사 / 이동할 대상이 필요하기 때문에 메모리를 할당합니다. function
는 주어진 호출 가능 function
소유권을 가져옵니다. 그러나 SortMyContainer
는 호출 가능 SortMyContainer
를 소유 할 필요가 없습니다. 그것은 단지 그것을 참조하고 있습니다. 그래서 여기에 function
사용 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