Eu entendo que a maneira correta de capturar this
(para modificar as propriedades do objeto) em um lambda é a seguinte:
auto f = [this] () { /* ... */ };
Mas estou curioso para saber a seguinte peculiaridade que vi:
class C {
public:
void foo() {
// auto f = [] () { // this not captured
auto f = [&] () { // why does this work?
// auto f = [&this] () { // Expected ',' before 'this'
// auto f = [this] () { // works as expected
x = 5;
};
f();
}
private:
int x;
};
A estranheza que me confunde (e gostaria de ser respondida) é por que o seguinte funciona:
auto f = [&] () { /* ... */ }; // capture everything by reference
E por que não consigo capturar explicitamente this
por referência:
auto f = [&this] () { /* ... */ }; // a compiler error as seen above.
this
não pode ser alterado, não é grande o suficiente para tornar uma referência mais rápida ... e de qualquer forma , ele não existe de fato , então nenhuma vida real, o que significa que qualquer referência a ele estaria pendente por definição.this
é um prvalue, não um lvalue.Respostas:
O motivo pelo qual
[&this]
não funciona é porque é um erro de sintaxe. Cada parâmetro separado por vírgula nolambda-introducer
é umcapture
:capture: identifier & identifier this
Você pode ver que isso
&this
não é permitido sintaticamente. O motivo pelo qual não é permitido é porque você nunca deseja capturarthis
por referência, pois é um pequeno ponteiro const. Você só gostaria de passá-lo por valor - então, a linguagem simplesmente não suporta a capturathis
por referência.Para capturar
this
explicitamente, você pode usar[this]
como olambda-introducer
.O primeiro
capture
pode ser umcapture-default
que é:capture-default: & =
Isso significa capturar automaticamente tudo o que eu uso, por referência (
&
) ou por valor (=
) respectivamente - no entanto, o tratamento dethis
é especial - em ambos os casos é capturado por valor pelas razões dadas anteriormente (mesmo com uma captura padrão de&
, o que geralmente significa captura por referência).5.1.2.7/8:
Portanto, o lambda atua como se fosse parte da função de membro envolvente ao usar nomes de membro (como em seu exemplo o uso do nome
x
), então ele irá gerar "usos implícitos"this
exatamente como faz uma função de membro.Assim você pode usar
[this]
,[&]
,[=]
ou[&,this]
como umlambda-introducer
para capturar othis
ponteiro por valor.No entanto
[&this]
e[=, this]
estão mal formados. No último caso, o gcc avisa com perdão sobre[=,this]
issoexplicit by-copy capture of ‘this’ redundant with by-copy capture default
ao invés de erros.fonte
[&]
se você estiver fazendo algo como criar um bloco a ser passado para uma estrutura de controle", mas capture explicitamente se você estiver produzindo um lambda que será usado para propósitos menos simples.[&]
é uma ideia horrível se o lambda vai sobreviver ao escopo atual. No entanto, muitos usos de lambdas são apenas maneiras de passar blocos para estruturas de controle, e o bloco não sobreviverá ao bloco que foi criado em escopo.this
é uma palavra-chave,this
não é um identificador.Porque o padrão não tem
&this
em listas de Capturas:N4713 8.4.5.2 Capturas:
lambda-capture: capture-default capture-list capture-default, capture-list capture-default: & = capture-list: capture...opt capture-list, capture...opt capture: simple-capture init-capture simple-capture: identifier &identifier this * this init-capture: identifier initializer &identifier initializer
Portanto, o padrão garante
this
e*this
é válido e&this
é inválido. Além disso, capturarthis
significa capturar*this
(que é um lvalue, o próprio objeto) por referência , em vez de capturarthis
ponteiro por valor !fonte
*this
captura o objeto por valor