¿Se permite eliminar un objeto lambda mientras se ejecuta la función lambda asociada, siempre que se tomen precauciones para no acceder a ninguno de los estados capturados después de la eliminación?
Considere el siguiente ejemplo:
int main() { int x = 1; std::function<void()> d; auto l = new auto([x, &d]() { fmt::print("x is {}\n", x); d(); fmt::print("I'm deleted\n"); }); d = [l](){ delete l; }; (*l)(); } Aquí, la lambda señalada por l se elimina a sí misma utilizando un enfoque similar al de Rube Goldberg a través de la función d 1 . Después de la eliminación, imprime un mensaje fijo que no accede a ningún estado capturado.
¿Es un comportamiento definido?
1 No pude encontrar una mejor manera de romper la dependencia circular entre el código lambda que necesita ver algo que se apunte a sí mismo: estoy abierto a mejores opciones.
Es posible que no haya una especificación explícita para esto en el estándar.
Aunque no se especifica que sea así, los tipos lambda son "esencialmente" como clases especiales. Quedándonos con esta analogía, la pregunta sería la misma que "¿Está bien definido delete this; ". La respuesta típica a esa pregunta es:
Siempre que tenga cuidado, está bien (no es malo) que un objeto se suicide (elimine esto).
Así es como defino "cuidadoso":
- Debe estar absolutamente 100% seguro de que este objeto se asignó a través de new (no por new[], ni por ubicación new, ni por un objeto local en la pila, ni por un ámbito de espacio de nombres / global, ni por un miembro de otro objeto; sino por llano ordinario nuevo).
- Debe estar absolutamente 100% seguro de que su función miembro será la última función miembro invocada en este objeto.
- Debe estar absolutamente 100% seguro de que el resto de su función miembro (después de eliminar esta línea) no toca ninguna parte de este objeto (incluida la llamada a otras funciones miembro o el contacto con cualquier miembro de datos). Esto incluye código que se ejecutará en destructores para cualquier objeto asignado en la pila que aún esté vivo.
- Debe estar absolutamente 100% seguro de que nadie toca el puntero después de eliminar esta línea. En otras palabras, no debe examinarlo, compararlo con otro puntero, compararlo con nullptr, imprimirlo, emitirlo, hacer nada con él.
Para traducir estos puntos a lambdas, "llamar a una función miembro" se convierte en "llamar a la lambda", "cualquier miembro de datos" se convierte en "cualquier captura". Su ejemplo satisface todos los puntos.