Eu sou novo no C ++ 11. Estou escrevendo a seguinte função lambda recursiva, mas ela não é compilada.
sum.cpp
#include <iostream>
#include <functional>
auto term = [](int a)->int {
return a*a;
};
auto next = [](int a)->int {
return ++a;
};
auto sum = [term,next,&sum](int a, int b)mutable ->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
int main(){
std::cout<<sum(1,10)<<std::endl;
return 0;
}
Erro de compilação:
Para obter mais informações, consulte a página de suporte do Microsoft Visual Studio.
sum.cpp: Na função lambda: sum.cpp: 18: 36: erro: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum
' não pode ser usado como uma função
versão gcc
gcc versão 4.5.0 20091231 (experimental) (GCC)
Mas se eu alterar a declaração sum()
como abaixo, ela funcionará:
std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
Alguém poderia por favor esclarecer isso?
mutable
palavra - chave está fazendo lá?std::function<int(int,int)> sum = [&](int a, int b) {
Respostas:
Pense na diferença entre a versão automática e a versão do tipo totalmente especificada. A palavra - chave auto infere seu tipo do que quer que seja inicializado, mas o que você está inicializando precisa saber qual é o seu tipo (nesse caso, o fechamento lambda precisa saber os tipos que está capturando). Algo como um problema de galinha e ovo.
Por outro lado, o tipo de um objeto de função totalmente especificado não precisa "saber" nada sobre o que está sendo atribuído a ele e, portanto, o fechamento do lambda também pode ser totalmente informado sobre os tipos que está capturando.
Considere esta pequena modificação do seu código e pode fazer mais sentido:
Obviamente, isso não funcionaria com auto . As funções lambda recursivas funcionam perfeitamente bem (pelo menos elas funcionam no MSVC, onde tenho experiência com elas), mas elas não são realmente compatíveis com a inferência de tipo.
fonte
auto
variável no inicializador. o tipo da variável automática ainda não é conhecido quando o inicializador está sendo processado.sum
outrastd::function<int(int, int)>
ou a especificação C ++ não se deu ao trabalho de inferir isso?O truque é alimentar a implementação lambda como um parâmetro , não por captura.
Todos os problemas em ciência da computação podem ser resolvidos por outro nível de indireção . Encontrei esse truque fácil pela primeira vez em http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/
Ele não requer C ++ 14, enquanto a questão é em C ++ 11, mas talvez interessante para a maioria.
Ir via
std::function
também é possível, mas pode resultar em código mais lento. Mas não sempre. Dê uma olhada nas respostas para std :: function vs templateIsso não é apenas uma peculiaridade do C ++, ele é mapeado diretamente para a matemática do cálculo lambda. Da Wikipedia :
fonte
function<>
. Não vejo por que alguém preferiria isso. Edit: É aparentemente mais rápido.error: use of ‘[...]’ before deduction of ‘auto’
- necessário especificar explicitamente o tipo de retorno (por outro lado, não precisava de mutação).Com o C ++ 14, agora é muito fácil criar um lambda recursivo eficiente sem a necessidade de sobrecarga adicional
std::function
, em apenas algumas linhas de código (com uma pequena edição do original para impedir que o usuário tire uma cópia acidental ):com o qual sua
sum
tentativa original se torna:No C ++ 17, com o CTAD, podemos adicionar um guia de dedução:
O que evita a necessidade da função auxiliar. Podemos apenas escrever
y_combinator{[](auto self, ...){...}}
diretamente.No C ++ 20, com o CTAD para agregados, o guia de dedução não será necessário.
fonte
std::forward<decltype(sum)>(sum)
vez dasum
última linha.operator()
então não há nada a ganhar com o encaminhamentosum
const
sobrecarga, caso o objeto de função fornecido tenha um nãoconst
operador de chamada. E use SFINAE e calculadonoexcept
para ambos. Além disso, não há mais necessidade da função maker no C ++ 17.auto sum
copia ... mas copia areference_wrapper
, que é a mesma coisa que tirar uma referência. Fazer isso uma vez na implementação significa que nenhum dos usos será copiado acidentalmente.Eu tenho outra solução, mas trabalho apenas com lambdas sem estado:
O truque aqui é que os lambdas podem acessar variáveis estáticas e você pode converter as sem estado em ponteiro de função.
Você pode usá-lo com lambdas padrão:
Seu trabalho no GCC 4.7
fonte
Você pode fazer uma função lambda chamar-se recursivamente. A única coisa que você precisa fazer é referenciá-lo através de um wrapper de função para que o compilador saiba que é o tipo de retorno e argumento (você não pode capturar uma variável - a própria lambda - que ainda não foi definida) .
Tenha muito cuidado para não ficar fora do escopo do invólucro f.
fonte
Para tornar o lambda recursivo sem usar classes e funções externas (
std::function
combinador como ponto fixo), pode-se usar a seguinte construção no C ++ 14 ( exemplo ao vivo ):impressões:
Observe que o tipo de resultado lambda deve ser especificado explicitamente.
fonte
Fiz um benchmark comparando uma função recursiva versus uma função lambda recursiva usando o
std::function<>
método de captura. Com as otimizações completas ativadas na versão 4.1 do clang, a versão lambda ficou significativamente mais lenta.Produz resultados:
(Nota: também confirmei com uma versão que utilizou as entradas do cin, para eliminar a avaliação do tempo de compilação)
Clang também produz um aviso do compilador:
O que é esperado e seguro, mas deve ser observado.
É ótimo ter uma solução em nossos cintos de ferramentas, mas acho que a linguagem precisará de uma maneira melhor de lidar com esse caso, se o desempenho for comparável aos métodos atuais.
Nota:
Como comentou um comentarista, parece que a versão mais recente do VC ++ encontrou uma maneira de otimizar isso até o ponto de desempenho igual. Talvez não precisemos de uma maneira melhor de lidar com isso, afinal (exceto o açúcar sintático).
Além disso, como algumas outras postagens de SO destacaram nas últimas semanas, o desempenho em
std::function<>
si pode ser a causa da função de desaceleração versus chamada diretamente, pelo menos quando a captura lambda é muito grande para caber em algunsstd::function
usos de espaço otimizado para bibliotecas para pequenos functores (Eu acho que meio que como as várias otimizações de cadeia curta?).fonte
Essa é uma implementação um pouco mais simples do operador de ponto de fixação, que torna um pouco mais óbvio exatamente o que está acontecendo.
fonte
std::function
pelo ponteiro de função (de núcleos, ele só funcionará com função normal e lambdas sem estado). Btwfib_nonr
deve aceitarfixpoint<int,int>
, se você usar ostd::function
seu exigir criação de nova cópia de*this
.Aqui está uma versão refinada da solução combinadora Y com base em uma proposta pela @Barry.
Para usar isso, pode-se fazer o seguinte
É semelhante à
let rec
palavra - chave no OCaml, embora não seja a mesma.fonte
C ++ 14: Aqui está um conjunto genérico de lambdas recursivo, sem estado / sem captura, sem captura de estado que gera todos os números de 1, 20
Se bem entendi, está usando a solução combinadora Y
E aqui está a versão da soma (n, m)
fonte
Aqui está a resposta final para o OP. De qualquer forma, o Visual Studio 2010 não oferece suporte à captura de variáveis globais. E você não precisa capturá-los porque a variável global é acessível globalmente por define. A resposta a seguir usa a variável local.
fonte
Você está tentando capturar uma variável (soma) que está no meio da definição. Isso não pode ser bom.
Eu não acho que lambdas C ++ 0x verdadeiramente auto-recursivas sejam possíveis. Você deve conseguir capturar outras lambdas, no entanto.
fonte
Essa resposta é inferior à de Yankes, mas ainda assim, aqui está:
fonte
reinterpret_cast
. Provavelmente, a melhor maneira no seu caso é criar alguma estrutura que substituadp_type
. Deveria ter campofp_type
, pode ser construídofp_type
e ter operador()
com argumentos comofp_type
. Isso estará próximo,std::function
mas permitirá o argumento de auto-referência.struct
também adicionaria um nível adicional de indireção. O exemplo funciona e o elenco é compatível com os padrões, não sei para que serve-1
.-1
eu não sabia quem o entregaria, mas acho que é porquereinterpret_cast
deveria ser usado como último recurso.cast
supostamente está garantido para o trabalho pela c ++ 11 standard. Usar umstruct
, aos meus olhos, poderia derrotar o uso de um objeto lambda. Afinal, o questruct
você propõe é um functor, utilizando um objeto lambda.std::function
e você terá algo parecido com o que eu tinha em mente. Provavelmente, isso terá desempenho semelhante à sua solução.Você precisa de um combinador de ponto fixo. Veja isso .
ou observe o seguinte código:
fonte