c++ - salida - parametros por valor y por referencia java
¿Deberían las funciones con plantilla tomar argumentos lambda por valor o por referencia de valor real? (3)
GCC 4.7 en el modo C ++ 11 me permite definir una función que toma una lambda de dos maneras diferentes:
// by value
template<class FunctorT>
void foo(FunctorT f) { /* stuff */ }
Y:
// by r-value reference
template<class FunctorT>
void foo(FunctorT&& f) { /* stuff */ }
Pero no:
// by reference
template<class FunctorT>
void foo(FunctorT& f) { /* stuff */ }
Sé que puedo desinstalar las funciones y solo tomar las funciones std ::, pero foo
es pequeño y está en línea, y me gustaría darle al compilador la mejor oportunidad para alinear las llamadas que hace dentro. De los primeros dos, ¿cuál es preferible para el rendimiento si sé específicamente que estoy pasando lambdas, y por qué no está permitido pasar lambdas al último?
Cuando creas una función lambda obtienes un objeto temporal. No puede enlazar referencias temporales a no validas. En realidad, no puede crear directamente un valor l que haga referencia a una función lambda.
Cuando declaras tu plantilla de función usando T&&
el tipo de argumento para la función será T const&
si pasas un objeto const
a la función, T&
le pasas un objeto l-value no const, y T
si lo pasas a temporal. Es decir, al pasar un temporal, la declaración de función tomará una referencia de valor r que se puede pasar sin mover un objeto. Al pasar el argumento explícitamente por valor, un objeto temporal se copia o mueve conceptualmente, aunque esta copia o movimiento generalmente se elimina. Si solo pasa objetos temporales a sus funciones, las primeras dos declaraciones harían lo mismo, aunque la primera declaración podría presentar un movimiento o una copia.
Esta es una buena pregunta, la primera parte: pasar por valor o reenviar el uso. Creo que la segunda parte (que tiene FunctorT&
como argumento) ha sido razonablemente respondida.
Mi consejo es este: use el reenvío solo cuando se conoce el objeto de función, de antemano, para modificar los valores en su cierre (o lista de captura). Mejor ejemplo: std :: shuffle. Toma un generador de números aleatorios uniformes (un objeto de función), y cada llamada al generador modifica su estado. El objeto de función se reenvía al algoritmo.
En cualquier otro caso, debería preferir pasar por valor. Esto no le impide capturar locales por referencia y modificarlos dentro de su función lambda. Eso funcionará justo como crees que debería. No debería haber gastos generales para copiar, como dice Dietmar. La inclusión también se aplicará y las referencias pueden optimizarse.
FunctorT&&
es una referencia universal y puede hacer coincidir cualquier cosa, no solo valores. Es la forma preferida de pasar elementos en plantillas C ++ 11, a menos que necesites absolutamente copias, ya que te permite utilizar un reenvío perfecto. Acceda al valor a través de std::forward<FunctorT>(f)
, que volverá a hacer un rvalue si fue antes, o lo dejará como un lvalue. Lea más here sobre el problema de reenvío y std::forward
y lea here para obtener una guía paso a paso sobre cómo std::forward
realmente funciona. This es también una lectura interesante.
FunctorT&
es solo una referencia FunctorT&
simple, y no puede vincular los temporales (el resultado de una expresión lambda) a eso.