É possível passar uma função lambda como um ponteiro de função? Nesse caso, devo estar fazendo algo incorretamente porque estou recebendo um erro de compilação.
Considere o seguinte exemplo
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Quando tento compilar isso , recebo o seguinte erro de compilação:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Essa é uma mensagem de erro para digerir, mas acho que o que estou tirando disso é que o lambda não pode ser tratado como um constexpr
modo, portanto, não posso transmiti-lo como um ponteiro de função? Eu tentei fazer x
const também, mas isso não parece ajudar.
c++
c++11
lambda
function-pointers
Cory Kramer
fonte
fonte
Respostas:
Um lambda só pode ser convertido em um ponteiro de função se não capturar, como mostra a seção padrão do C ++ 11
5.1.2
[expr.prim.lambda] ( ênfase minha ):Observe que o cppreference também aborda isso em sua seção sobre funções do Lambda .
Portanto, as seguintes alternativas funcionariam:
e assim seria:
e como 5gon12eder aponta, você também pode usá-lo
std::function
, mas observe questd::function
é muito pesado , portanto, não é uma troca barata.fonte
void*
como o único parâmetro. É normalmente chamado de "ponteiro do usuário". Também é relativamente leve, mas tende a exigirmalloc
algum espaço.A resposta de Shafik Yaghmour explica corretamente por que o lambda não pode ser passado como ponteiro de função se tiver uma captura. Eu gostaria de mostrar duas correções simples para o problema.
Use em
std::function
vez de ponteiros de função bruta.Esta é uma solução muito limpa. Observe, no entanto, que ele inclui alguma sobrecarga adicional para o apagamento do tipo (provavelmente uma chamada de função virtual).
Use uma expressão lambda que não captura nada.
Como seu predicado é realmente apenas uma constante booleana, o seguinte rapidamente resolveria o problema atual. Veja esta resposta para uma boa explicação de por que e como isso está funcionando.
fonte
glutReshapeFunc
.std::function
ou um lambda - por que não? No mínimo, é uma sintaxe mais legível. Normalmente, é necessário usar um ponteiro de função para interagir com as bibliotecas C (na verdade, com qualquer biblioteca externa) , e você não pode modificá-lo para aceitar uma função std :: ou lambda.Expressões lambda, mesmo as capturadas, podem ser manipuladas como um ponteiro de função (ponteiro para a função membro).
É complicado porque uma expressão lambda não é uma função simples. Na verdade, é um objeto com um operador ().
Quando você é criativo, pode usar isso! Pense em uma classe "function" no estilo de std :: function. Se você salvar o objeto, também poderá usar o ponteiro de função.
Para usar o ponteiro de função, você pode usar o seguinte:
Para criar uma classe que possa começar a funcionar como uma "std :: function", primeiro você precisa de uma classe / estrutura que possa armazenar o objeto e o ponteiro de função. Você também precisa de um operador () para executá-lo:
Com isso, agora você pode executar lambdas capturadas e não capturadas, exatamente como você está usando o original:
Este código funciona com o VS2015
Atualização 04.07.17:
fonte
operator()
implementação; portanto, se eu estiver lendo direito, não acho que funcionaria chamar o lambda usando um ponteiro de função no estilo C , seria? Foi isso que a pergunta original pediu.A captura de lambdas não pode ser convertida em ponteiros de função, pois esta resposta apontou.
No entanto, muitas vezes é muito difícil fornecer um ponteiro de função para uma API que aceita apenas um. O método mais citado para fazer isso é fornecer uma função e chamar um objeto estático.
Isso é entediante. Levamos essa idéia adiante e automatizamos o processo de criação
wrapper
e facilitar a vida.E use-o como
Viver
Isto é essencialmente declarar uma função anônima em cada ocorrência de
fnptr
.Observe que as invocações de
fnptr
sobrescrever oscallable
callables fornecidos anteriormente escritos do mesmo tipo. Nós remediar esta situação, até certo ponto, com oint
parâmetroN
.fonte
Um atalho para usar um lambda como um ponteiro de função C é este:
Usando Curl como exemplo ( informações de depuração de curl )
fonte
+
truque que você recebe).Embora a abordagem do modelo seja inteligente por vários motivos, é importante lembrar o ciclo de vida do lambda e as variáveis capturadas. Se alguma forma de ponteiro lambda for usada e o lambda não for uma continuação descendente, somente um lambda de cópia [=] deve ser usado. Ou seja, mesmo assim, capturar um ponteiro para uma variável na pilha não é SEGURO se a vida útil desses ponteiros capturados (desenrolar a pilha) for menor que a vida útil da lambda.
Uma solução mais simples para capturar um lambda como um ponteiro é:
por exemplo,
new std::function<void()>([=]() -> void {...}
Lembre-se de mais tarde,
delete pLamdba
para garantir que você não vaze a memória lambda. O segredo para perceber aqui é que os lambdas podem capturar lambdas (pergunte a si mesmo como isso funciona) e também que, parastd::function
funcionar genericamente, a implementação lambda precisa conter informações internas suficientes para fornecer acesso ao tamanho dos dados lambda (e capturados) ( é por isso quedelete
deve funcionar [executando destruidores de tipos capturados]).fonte
new
função - std :: já armazena o lambda na pilha E evita a necessidade de lembrar de chamar delete.Não é uma resposta direta, mas uma pequena variação para usar o padrão de modelo "functor" para ocultar as especificidades do tipo lambda e manter o código agradável e simples.
Eu não tinha certeza de como você queria usar a classe decide, então tive que estender a classe com uma função que a utiliza. Veja o exemplo completo aqui: https://godbolt.org/z/jtByqE
A forma básica da sua classe pode ser assim:
Onde você passa o tipo da função como parte do tipo de classe usado como:
Mais uma vez, eu não sabia por que você
x
estava capturando , fazia mais sentido (para mim) ter um parâmetro que você passasse para o lambda) para que você pudesse usar como:Veja o link para um exemplo completo
fonte
Como foi mencionado pelos outros, você pode substituir a função Lambda em vez do ponteiro de função. Eu estou usando esse método na minha interface C ++ para o solucionador de F77 ODE RKSUITE.
fonte