C++
std :: function: Para envolver cualquier elemento que sea llamable
Buscar..
Uso simple
#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 :: función utilizada con std :: bind
Piense en una situación en la que necesitamos devolver una función con argumentos. std::function
utilizada con std::bind
proporciona una construcción de diseño muy potente como se muestra a continuación.
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 :: función con lambda y 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;
}
`function` sobrecarga
std::function
puede causar una sobrecarga significativa. Debido a que std::function
tiene [valor semántico] [1], debe copiar o mover el llamable dado en sí mismo. Pero como puede tomar callables de un tipo arbitrario, con frecuencia tendrá que asignar memoria dinámicamente para hacer esto.
Algunas implementaciones de function
se denominan "optimización de objetos pequeños", donde los tipos pequeños (como punteros de función, punteros de miembro o functores con muy poco estado) se almacenarán directamente en el objeto de function
. Pero incluso esto solo funciona si el tipo es noexcept
mover constructible. Además, el estándar de C ++ no requiere que todas las implementaciones proporcionen una.
Considera lo siguiente:
//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);
}
Un parámetro de plantilla sería la solución preferida para SortMyContainer
, pero supongamos que esto no es posible o deseable por cualquier motivo. SortMyContainer
no necesita almacenar pred
más allá de su propia llamada. Y, sin embargo, pred
puede asignar memoria si el functor que se le asigna es de algún tamaño no trivial.
function
asigna memoria porque necesita algo para copiar / mover; function
se apropia de lo callable que se le da. Pero SortMyContainer
no necesita poseer el invocable; es solo referenciarlo Así que usar la function
aquí es una exageración; Puede ser eficiente, pero puede no serlo.
No hay un tipo de función de biblioteca estándar que simplemente haga referencia a un llamable. Por lo tanto, habrá que encontrar una solución alternativa, o puede elegir vivir con los gastos generales.
Además, la function
no tiene medios efectivos para controlar de dónde provienen las asignaciones de memoria para el objeto. Sí, tiene constructores que toman un allocator
, pero [muchas implementaciones no los implementan correctamente ... o incluso en absoluto ] [2].
Los constructores de function
que toman un allocator
ya no forman parte del tipo. Por lo tanto, no hay manera de gestionar la asignación.
Llamar a una function
también es más lento que llamar directamente al contenido. Dado que cualquier instancia de function
podría contener cualquier llamada, la llamada a través de una function
debe ser indirecta. La sobrecarga de la function
de llamada está en el orden de una llamada de función virtual.
Enlace std :: función a diferentes tipos de llamada
/*
* 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);
}
Salida:
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
Almacenando argumentos de funciones en std :: tuple
Algunos programas necesitan almacenar los argumentos para futuras llamadas de alguna función.
Este ejemplo muestra cómo llamar a cualquier función con argumentos almacenados en 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);
}
Salida:
foo_fn called. x = 1 y = 5 z = 10 res=16