Existem perguntas semelhantes em torno de:
É seguro passar uma lambda que está fora do escopo "por valor"?
- Sim, porque o fechamento é copiado/movido
Devo passar uma referência lambda por const ?
- Sim, isso é útil para evitar copiar o estado. Mas você não pode passar um lambda mutável, que tem um não-const
operator()
- Sim, isso é útil para evitar copiar o estado. Mas você não pode passar um lambda mutável, que tem um não-const
Minha pergunta é uma mistura das duas:
há uma classe de armazenamento que recebe um lambda por referência e, de alguma forma, o armazena, com uma assinatura de método, como na pergunta vinculada:
template <class Function>
void storeCallback(const Function& f)
É seguro chamá-lo da seguinte maneira?
void foo(Storage& o) {
o.storeCallback([](){std::cout << "hi!";});
}
void bar(Storage& o){
o.callCallback();
}
O risco que vejo é que o lambda saia do escopo quando foo
returns. Então a referência seria inválida quando, em algum momento no futuro, o retorno de chamada armazenado for chamado?
No caso específico em que estou interessado, que motivou esta pergunta, as especificidades de como o lambda é armazenado na classe de armazenamento são, que ele é passado para outro método que recebe um std::function
by value como parâmetro. Essa conversão de referência para std::function
de alguma forma o torna seguro? Ou ele já era seguro independente de como é armazenado?
Os std::function
documentos afirmam
Instâncias de
std::function
podem armazenar, copiar e invocar qualquer destino Callable CopyConstructible -- funções (por meio de ponteiros para elas), expressões lambda , expressões de ligação ou outros objetos de função, bem como ponteiros para funções de membro e ponteiros para membros de dados.
Isso me parece que std::function
o torna seguro porque ele copia o lambda de qualquer maneira, mesmo que tenha sido passado por referência.
No código que você escreveu, você definitivamente está usando algum tipo de apagamento de tipo (como
std::function
) porque você está armazenando um tipo desconhecido (lambda). Se esse armazenamento fossestd::function
de fato, você tem a garantia de ter uma cópia do seu lambda. Isso ocorre porque a cópia para dentrostd::function
aconteceria antes defoo
sair do escopo. Se seu armazenamento estivesse salvando uma referência ao seu lambda em vez de copiá-lo, comostd::function_ref
faz, isso causaria um comportamento indefinido. Observe, no entanto, que como seu lambda não captura nada, isso provavelmente lhe daria um resultado correto, embora o comportamento seja realmente indefinido.Além disso, não confunda captura por referência com passagem por referência. A última geralmente é segura, a primeira geralmente não é. Se você tivesse isso,
a referência a
name
ficaria pendurada. Pode ser ainda mais perigoso se você estiver capturando por referência dentro de uma função membro, porque você pode capturar acidentalmentethis
por referência e isso é UB esperando para acontecer.