c++ - ¿Por qué el operador de llamada de la lambda está implícitamente implícito?
c++11 const (3)
Creo que es simplemente evitar la confusión cuando una variable dentro de un lambda no se refiere a lo que originalmente se capturó. Léxicamente, tal variable es como si estuviera en el alcance de su "original". La copia es principalmente para permitir extender la vida útil del objeto. Cuando una captura no es por copia, se refiere al original y las modificaciones se aplican al original, y no hay confusión debido a dos objetos diferentes (uno de los cuales se introduce implícitamente), y está permitido por el operador de llamada a la función const de lambda.
Tengo una pequeña "expresión lambda" en la siguiente función:
int main()
{
int x = 10;
auto lambda = [=] () { return x + 3; };
}
A continuación se muestra la "clase de cierre anónimo" generada para la expresión lambda anterior.
int main()
{
int x = 10;
class __lambda_3_19
{
public: inline /*constexpr */ int operator()() const
{
return x + 3;
}
private:
int x;
public: __lambda_3_19(int _x) : x{_x}
{}
};
__lambda_3_19 lambda = __lambda_3_19{x};
}
El "operador ()" del cierre generado por el compilador es implícitamente constante. ¿Por qué el comité estándar lo hizo const
por defecto?
De cppreference
A menos que la palabra clave
mutable
se haya utilizado en la expresión lambda, el operador de llamada de función está cualificado constantemente y los objetos que fueron capturados por copia no son modificables desde el interior de esteoperator()
En su caso, no hay nada que, capturado por copia, sea modificable.
Supongo que si escribes algo como
int x = 10;
auto lambda = [=] () mutable { x += 3; return x; };
la const
debería desaparecer
- EDITAR -
La OP precisa
Ya sabía que agregar mutable resolverá el problema. La pregunta es que quiero entender la razón detrás de hacer que la lambda sea inmutable por defecto.
No soy un abogado de idiomas, pero esto me parece obvio: si haces operator()
no const
, no puedes hacer algo como
template <typename F>
void foo (F const & f)
{ f(); }
// ...
foo([]{ std::cout << "lambda!" << std::endl; });
Quiero decir ... si el operator()
no es const
, no puede usar las lambdas que pasan como referencia const
.
Y cuando no es estrictamente necesario, debe ser una limitación inaceptable.
Encontré este paper de Herb Sutter en open-std.org que trata este asunto.
La extraña pareja: captura por const de valor y peculiaridad mutable
Considere este ejemplo de Strawman, donde el programador captura una variable local por valor e intenta modificar el valor capturado (que es una variable miembro del objeto lambda):
int val = 0; auto x = [=]( item e ) // look ma, [=] means explicit copy { use( e, ++val ); }; // error: count is const, need ‘mutable’ auto y = [val]( item e ) // darnit, I really can’t get more explicit { use( e, ++val ); }; // same error: count is const, need ‘mutable’
Esta característica parece haber sido agregada debido a la preocupación de que el usuario podría no darse cuenta de que recibió una copia, y en particular, dado que las lambdas son copiables, podría estar cambiando una copia de la lambda diferente.
La cita y el ejemplo anteriores indican por qué el Comité de Normas podría haberlo hecho const
de forma predeterminada y debe ser mutable
para cambiarlo.