significado - Creando lambda de C++ por su tipo
tipos de variables en c++ pdf (4)
Quiero una manera de hacer functor de función. Ahora estoy tratando de ajustar la función call por la función lambda y crear una instancia más tarde. Pero el compilador dice que el constructor lambda es eliminado. Entonces, ¿hay alguna manera de compilar este código? O tal vez de otra manera para eso?
#include <iostream>
void func()
{
std::cout << "Hello";
}
auto t = []{ func(); };
typedef decltype(t) functor_type;
template <class F>
void functor_caller()
{
F f;
f();
}
int main()
{
functor_caller<functor_type>();
return 0;
}
Ahora me sale tal error de compilación:
error: use of deleted function ''<lambda()>::<lambda>()''
error: a lambda closure type has a deleted default constructor
En mi opinión, la única forma es usar macro:
#define WRAP_FUNC(f) /
struct f##_functor /
{ /
template <class... Args > /
auto operator()(Args ... args) ->decltype(f(args...)) /
{ /
return f(args...); /
} /
};
entonces
WRAP_FUNC(func);
y luego (en principal)
functor_caller<func_functor>()
El código no tiene sentido. Imagina que tienes una lambda de captura como esta:
{
int n = 0;
auto t = [&n](int a) -> int { return n += a; };
}
¿Qué podría significar construir por defecto un objeto de tipo decltype(t)
?
Como lo sugiere @Matthieu, podría envolver la lambda en un objeto de function
:
std::function<int(int)> F = t;
O puede crear una plantilla de su sitio de llamada directamente según el tipo de lambda (o cualquier entidad llamable):
template <typename F>
int compute(int a, int b, F f)
{
return a * f(b); // example
}
Uso: int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }
int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }
Si es posible, el segundo estilo es preferible, ya que la conversión a std::function
es una operación no trivial y potencialmente costosa, como lo es la llamada a la función real a través del objeto resultante. Sin embargo, si necesita almacenar una colección uniforme de entidades anulables heterogéneas, entonces la std::function
puede ser la solución más fácil y conveniente.
Lambdas no tiene constructores por defecto. Siempre. Los únicos constructores a los que a veces dan acceso es (dependiendo de lo que capturen) los constructores de copia y / o movimiento.
Si creas un functor sin un constructor público predeterminado, obtendrás el mismo error.
En C ++ 17 puede resolver esto con constexpr
lambda y operator+
para el puntero de descomposición a función. Un tipo que lleva un puntero a una función y lo invoca es fácil con los parámetros de la plantilla auto
.
En C ++ 11 tienes que ser un poco hacky.
template<class F>
struct stateless_lambda_t {
static std::aligned_storage_t< sizeof(F), alignof(F) >& data() {
static std::aligned_storage_t< sizeof(F), alignof(F) > retval;
return retval;
};
template<class Fin,
std::enable_if_t< !std::is_same< std::decay_t<Fin>, stateless_lambda_t >{}, int> =0
>
stateless_lambda_t( Fin&& f ) {
new ((void*)&data()) F( std::forward<Fin>(f) );
}
stateless_lambda_t(stateless_lambda_t const&)=default;
template<class...Args>
decltype(auto) operator()(Args&&...args)const {
return (*static_cast<F*>( (void*)&data() ))(std::forward<Args>(args)...);
}
stateless_lambda_t() = default;
};
template<class F>
stateless_lambda_t<std::decay_t<F>> make_stateless( F&& fin ) {
return {std::forward<F>(fin)};
}
Ahora podemos:
auto t = make_stateless([]{ func(); });
y tu código funciona.
Una static_assert
o SFINAE de que la F
es en realidad un tipo vacío podría ser una buena idea. Ya sabes, por la calidad.
El uso de las funciones de C ++ 14 se puede reemplazar con decltype
manual y palabras de nombre de ::type
y de typename
. Esta respuesta se escribió originalmente para una pregunta de C ++ 14 que se cerró como un duplicado de esta.
No.
Sin embargo, creo que las lambdas se pueden copiar, por lo que su functor_caller
podría tomar un argumento para inicializar su atributo.
Aún así, en lugar de reinventar la rueda, usaría std::function
lugar.
Podemos asumir que la lambda siempre estará vacía, por lo tanto, podemos hacer un lanzamiento desde otro tipo vacío, ya que ambos tienen el mismo diseño de memoria. Así que construimos una clase de contenedor que hace un objeto de función constructible por defecto:
template<class F>
struct wrapper
{
static_assert(std::is_empty<F>(), "Lambdas must be empty");
template<class... Ts>
auto operator()(Ts&&... xs) const -> decltype(reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...))
{
return reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...);
}
};
Ahora se agrega una aserción estática para asegurarse de que la lambda esté siempre vacía. Este debería ser siempre el caso (ya que se requiere que decaiga a un puntero de función), pero el estándar no lo garantiza explícitamente. Así que usamos el aserto para, al menos, detectar la loca implementación de las lambdas. Luego, simplemente enviamos la clase de envoltorio a la lambda ya que ambos están vacíos.
Finalmente la lambda podría construirse así:
template <class F>
void function_caller()
{
wrapper<F> f;
f();
}