Redefiniendo lambdas no permitido en C++ 11, ¿por qué?
c++11 language-lawyer (5)
Ejemplo:
#include <functional>
int main() {
auto test = []{};
test = []{};
return 0;
}
Esto emite el siguiente mensaje de error en gcc 4.7.2:
test.cpp: In function ‘int main()’:
test.cpp:5:13: error: no match for ‘operator=’ in ‘test = <lambda closure object>main()::<lambda()>{}’
test.cpp:5:13: note: candidate is:
test.cpp:4:16: note: main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&) <deleted>
test.cpp:4:16: note: no known conversion for argument 1 from ‘main()::<lambda()>’ to ‘const main()::<lambda()>&’
Del estándar 5.1.2.3 (énfasis mío):
Una implementación puede definir el tipo de cierre de manera diferente a la que se describe a continuación, siempre que esto no altere el comportamiento observable del programa que no sea el cambio:
- el tamaño y / o la alineación del tipo de cierre,
- si el tipo de cierre es trivialmente copiable (Cláusula 9)
- si el tipo de cierre es una clase de disposición estándar (Cláusula 9), o
- si el tipo de cierre es una clase POD (Cláusula 9).
Por lo que puedo decir, esto es con lo que me estoy enfrentando. Está intentando usar un operador de asignación eliminado y está fallando. Tengo curiosidad por saber si hay una solución fácil, y en términos más generales, cuál es la razón motivadora para permitir que se omita la copia constructiva para las lambdas en general.
El tipo de expresión lambda (que también es el tipo de objeto de cierre) es un tipo de clase no sindical sin nombre único
Entonces, es como si estuvieras haciendo lo siguiente:
struct {} a;
struct {} b;
a = b; // error, type mismatch
Use std::function
si desea asignar diferentes lambdas con la misma firma a la misma variable.
std::function<void()> f = []{};
f = []{}; //ok
La razón por la que no puede hacer esto es porque el operador de asignación de copia para la expresión lambda se declara eliminado, consulte la sección 5.1.2 / 20 del estándar. Para una definición más clara (para las definiciones inusuales de claro), vea este ejemplo de código
template<class T> void f(T x1)
{
T x2 = x1; // copy constructor exists, this operation will succeed.
x2 = x1; // assignment operator, deleted and will cause an error
}
int main()
{
f([]{});
return 0;
}
Otras respuestas han señalado que cada lambda tiene un tipo único, pero esta no es la razón por la que está obteniendo ese error. Este ejemplo muestra que incluso si las dos lambdas tienen el mismo tipo, aún no puede copiarlo. Sin embargo, puedes copiarlo a una nueva variable. Esta es la razón por la cual su mensaje de error se queja de la falta de operator=
y no de que sus tipos sean diferentes. Aunque cada lambda que tiene su propio tipo tampoco te ayuda mucho.
Lambda no se puede redefinir porque cada lambda es de un tipo diferente, anónimo e incompatible. Se pueden copiar solo si los pasa a una función de plantilla (como std::function
ctor) que podría deducir ese tipo.
Pareces pensar que esas dos lambdas tienen el mismo tipo, pero eso no es verdad. Cada uno crea su propio tipo:
#include <functional>
#include <type_traits>
#include <iostream>
int main() {
auto test = []{};
auto test2 = []{};
std::cout << std::is_same< decltype( test ), decltype( test2 ) >::value << std::endl;
return 0;
}
imprimirá 0
. Por supuesto, el mensaje de error que está obteniendo del compilador podría ser un poco más claro a este respecto ...
Si pudiéramos asignar una lambda a otra lambda de un tipo diferente, ¿cómo copiamos los cuerpos de funciones / definiciones de esa lambda a la otra? Si fuéramos tan tercos, podríamos usar algún miembro tipo std::function
para ser el que se copiará. Pero eso sería en contra de la vieja regla de C ++ de no pagar, bla, bla ...