Breve exemplo:
#include <iostream>
int main()
{
int n;
[&](){n = 10;}(); // OK
[=]() mutable {n = 20;}(); // OK
// [=](){n = 10;}(); // Error: a by-value capture cannot be modified in a non-mutable lambda
std::cout << n << "\n"; // "10"
}
A pergunta: por que precisamos do mutable
palavra chave? É bem diferente da passagem tradicional de parâmetros para funções nomeadas. Qual é a lógica por trás?
Fiquei com a impressão de que todo o objetivo da captura por valor é permitir que o usuário altere o temporário - caso contrário, estou quase sempre melhor usando a captura por referência, não?
Alguma iluminação?
(Estou usando o MSVC2010, a propósito. AFAIK isso deve ser padrão)
const
por padrão!const
por padrão.Respostas:
Requer
mutable
porque, por padrão, um objeto de função deve produzir o mesmo resultado toda vez que é chamado. Essa é a diferença entre uma função orientada a objeto e uma função que usa uma variável global de maneira eficaz.fonte
void f(const std::function<int(int)> g)
. Como posso garantir queg
é realmente referencialmente transparente ?g
fornecedor pode ter usado demutable
qualquer maneira. Então eu não vou saber. Por outro lado, se o padrão for não-const
e as pessoas tiverem que adicionar objetos emconst
vez demutable
funcionar, o compilador pode realmente impor aconst std::function<int(int)>
peça e agoraf
pode assumir queg
éconst
, não?Seu código é quase equivalente a isso:
Portanto, você pode pensar em lambdas como gerando uma classe com operator () cujo padrão é const, a menos que você diga que é mutável.
Você também pode pensar em todas as variáveis capturadas dentro de [] (explícita ou implicitamente) como membros dessa classe: cópias dos objetos para [=] ou referências aos objetos para [&]. Eles são inicializados quando você declara seu lambda como se houvesse um construtor oculto.
fonte
const
oumutable
lambda olharia como se implementado como tipos definidos pelo usuário equivalentes, a questão é (como no título e elaborado pelo OP nos comentários) por queconst
é o padrão, de modo que este não atender.A questão é: é "quase"? Um caso de uso frequente parece retornar ou passar lambdas:
Eu acho que
mutable
não é um caso de "quase". Considero "captura por valor" como "permitir que eu use seu valor após a morte da entidade capturada" em vez de "permitir que eu altere uma cópia dele". Mas talvez isso possa ser discutido.fonte
const
? Que finalidade isso alcança?mutable
parece deslocado aqui, quando nãoconst
é o padrão em "quase" (: P) em todo o resto do idioma.const
era o padrão, pelo menos as pessoas seriam forçadas a considerar const-correção: /const
que pudessem chamá-lo se o objeto lambda é ou não const. Por exemplo, eles poderiam passar para uma função usando astd::function<void()> const&
. Para permitir que o lambda altere suas cópias capturadas, nos documentos iniciais, os membros dos dados do fechamento foram definidosmutable
internamente automaticamente. Agora você deve inserir manualmentemutable
a expressão lambda. Ainda não encontrei uma lógica detalhada.FWIW, Herb Sutter, um membro bem conhecido do comitê de padronização C ++, fornece uma resposta diferente para essa pergunta em questões de correção e usabilidade do Lambda :
Seu artigo é sobre por que isso deve ser alterado no C ++ 14. É curto, bem escrito, vale a pena ler se você quiser saber "o que está em mente [do membro do comitê]" com relação a esse recurso em particular.
fonte
Você precisa pensar qual é o tipo de fechamento da sua função Lambda. Toda vez que você declara uma expressão Lambda, o compilador cria um tipo de fechamento, que nada mais é do que uma declaração de classe sem nome com atributos ( ambiente em que a expressão Lambda foi declarada) e a chamada de função
::operator()
implementada. Quando você captura uma variável usando copiar por valor , o compilador cria um novoconst
atributo no tipo de fechamento, para que você não possa alterá-la dentro da expressão Lambda porque é um atributo "somente leitura", é por isso que eles chamá-lo de " fechamento " ", porque, de alguma forma, você está fechando sua expressão Lambda, copiando as variáveis do escopo superior para o escopo do Lambda.mutable
, a entidade capturada se tornará umnon-const
atributo do seu tipo de fechamento. É isso que faz com que as alterações feitas na variável mutável capturada pelo valor não sejam propagadas para o escopo superior, mas permaneçam dentro do Lambda com monitoração de estado. Sempre tente imaginar o tipo de fechamento resultante da sua expressão Lambda, que me ajudou muito, e espero que possa ajudá-lo também.fonte
Veja este rascunho , em 5.1.2 [expr.prim.lambda], subcláusula 5:
Editar no comentário do litb: Talvez eles pensassem em captura por valor, para que mudanças externas nas variáveis não sejam refletidas dentro do lambda? As referências funcionam nos dois sentidos, então essa é a minha explicação. Não sei se é bom.
Editar no comentário do kizzx2: A maioria das vezes em que um lambda deve ser usado é como um functor para algoritmos. O
const
ness padrão permite que ele seja usado em um ambiente constante, assim comoconst
funções normais qualificadas podem ser usadas lá, mas as funções nãoconst
qualificadas não podem. Talvez eles tenham pensado em torná-lo mais intuitivo para esses casos, que sabem o que se passa em sua mente. :)fonte
const
por padrão? Eu já tenho uma nova cópia, parece estranho não me deixar alterá-la - especialmente não é algo principalmente errado com ela - eles só querem que eu adicionemutable
.var
como uma palavra-chave para permitir que mudanças e constantes sejam o padrão para todo o resto. Agora não temos, então temos que conviver com isso. OMI, C ++ 2011 saiu muito bem, considerando tudo.n
não é temporário. n é um membro do objeto de função lambda que você cria com a expressão lambda. A expectativa padrão é que chamar seu lambda não modifique seu estado; portanto, é constante impedir que você modifique acidentalmenten
.fonte
Você precisa entender o que captura significa! está capturando não passando argumentos! vamos dar uma olhada em alguns exemplos de código:
Como você pode ver, embora
x
tenha sido alterado para20
o lambda, ainda está retornando 10 (x
ainda está5
dentro do lambda) Mudarx
dentro do lambda significa alterar o próprio lambda a cada chamada (o lambda está mudando a cada chamada). Para reforçar a correção, o padrão introduziu amutable
palavra - chave. Ao especificar um lambda como mutável, você está dizendo que cada chamada ao lambda pode causar uma alteração no próprio lambda. Vamos ver outro exemplo:O exemplo acima mostra que, ao tornar o lambda mutável, a alteração
x
dentro do lambda " modifica " o lambda a cada chamada com um novo valorx
que não tem nada a ver com o valor real dax
função principalfonte
Existe agora uma proposta para aliviar a necessidade de
mutable
declarações lambda: n3424fonte
mutable
é uma palavra-chave em C ++.Para estender a resposta do Puppy, as funções lambda devem ser puramente funções . Isso significa que toda chamada que recebe um conjunto de entrada exclusivo sempre retorna a mesma saída. Vamos definir entrada como o conjunto de todos os argumentos mais todas as variáveis capturadas quando o lambda é chamado.
Em funções puras, a saída depende apenas da entrada e não de algum estado interno. Portanto, qualquer função lambda, se pura, não precisa alterar seu estado e, portanto, é imutável.
Quando um lambda captura por referência, a gravação em variáveis capturadas é uma pressão sobre o conceito de função pura, porque tudo o que uma função pura deve fazer é retornar uma saída, embora o lambda certamente não sofra mutação porque a gravação acontece com variáveis externas. Mesmo nesse caso, um uso correto implica que, se o lambda for chamado com a mesma entrada novamente, a saída será a mesma todas as vezes, apesar desses efeitos colaterais nas variáveis by-ref. Tais efeitos colaterais são apenas maneiras de retornar alguma entrada adicional (por exemplo, atualizar um contador) e podem ser reformulados para uma função pura, por exemplo, retornar uma tupla em vez de um único valor.
fonte