2010-12-13 8 views
16

Al usar funciones lambda, digamos que usted decide copiar una variable (con la notación [=]). Si nunca vuelve a hacer referencia a esa variable, ¿el compilador puede moverla al objeto de función resultante?Moviéndose con lambdas

Editar: Por ejemplo, he escrito un fragmento para mover llamadas a través de subprocesos. Aquí hay una muestra que lo hace.

extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) { 
    std::string file(filename); 
    p->make_call([=]() { 
     p->file_updated(std::move(file), offset, added); 
    }); 
} 

Pero está claro que la variable de archivo no tiene que vivir más allá de la definición-lambda y, de hecho, la única lambda se llama una vez, por lo que se trasladó la copia.

+3

Supongo que esto es demasiado difícil de entender en general para los compiladores. ¿Puedes proporcionar un código de ejemplo? – fredoverflow

+0

Existe la regla "como si", por supuesto. Pero supongo que estás pensando en un caso en el que el copiador y/o el dispositivo móvil tengan efectos secundarios, por lo que puedes notar la diferencia. Interesante pregunta. – aschepler

+0

@aschepler: si recuerdo correctamente, el compilador puede ignorar los efectos secundarios de copiar y mover constructores. @DeadMG: me parece que podría ser optimizado, ¿lo has probado? –

Respuesta

12

Si nunca vuelve a hacer referencia a esa variable, ¿el compilador puede moverla al objeto de función resultante?

No. La única situación en la que el compilador puede reemplazar una copia con un movimiento son exactamente las mismas situaciones en las que se permite realizar una elisión de copia. Estas situaciones incluyen devolver un objeto local por valor o inicializar un objeto con un valor temporal. En estos casos, el compilador puede eludir la copia haciendo que el origen y el objetivo sean el mismo objeto. Si el compilador no puede hacer eso por la razón que sea, tiene que considerar el objeto fuente como un valor r con respecto a la resolución de sobrecarga para seleccionar el constructor apropiado para el objeto objetivo. En su caso, sin embargo, el archivo es un Lvalue y ninguno de los casos anteriores se aplica. Deberías usar un movimiento explícito.

Desafortunadamente, C++ 11 no tiene una sintaxis para una "captura de movimiento". En mi humilde opinión, es una pena. Pero std :: bind es compatible con esto. Debería ser posible combinar std :: unen con una expresión lambda como esto:

void foo(char const* p) { 
    string s = p; 
    auto fun = bind([](string const& s){ 
     ... 
    },move(s)); 
    fun(); 
} 

para que la cadena se mueve en el objeto función.

Si la intención de llamar a esta función sólo una vez y desea mover la cuerda fuera del objeto de la función de nuevo, puede utilizar una referencia no const:

void foo(char const* p) { 
    string s = p; 
    auto fun = bind([](string & s) { 
     some_other_func(move(s)); 
    },move(s)); 
    fun(); 
} 

Tenga en cuenta que, si no lo hace desee utilizar unen aquí, pero dejar que el constructor del objeto lambda crear una copia de s, moviendo la cuerda hacia fuera del objeto función requiere la palabra clave mutable:

void foo(char const* p) { 
    string s = p; 
    auto fun = [=]() mutable { 
     //   ^^^^^^^ 
     some_other_func(move(s)); 
    }; 
    fun(); 
} 

porque de lo contrario el operador del tipo de cierre() función será const- calificado que a su vez hace s una cadena constificada.

En C++ 14 la cláusula de captura lambda se volvió un poco más flexible. Ahora podemos escribir

void foo(char const* p) { 
    string s = p; 
    auto fun = [s=move(s)]() mutable { // #1 
     some_other_func(move(s));  // #2 
    }; 
    fun(); 
} 

donde # 1 se mueve el valor de cadena en un objeto lambda y # 2 se mueve la cadena de valor fuera (dependiendo de cómo some_other_func se declara exactamente).

+0

Un compilador de C++ puede hacer cualquier optimización que se siente, siempre que el comportamiento observable sea el mismo. En este caso específico, podría mover el objeto sin que usted pueda decirlo, por lo que está permitido. Esto hace que tu respuesta sea errónea. – nwp

+0

@nwp: A todos los efectos, no estoy de acuerdo. El comportamiento observable incluye el hecho de si se invoca una copia o un movimiento, asumiendo que las funciones se comporten de manera diferente, lo cual es, por supuesto, el propósito de un movimiento. La regla de si ... no lo ayuda a reducir los costos aquí. – sellibitze