Função que retorna uma expressão lambda

88

Gostaria de saber se é possível escrever uma função que retorna uma função lambda em C ++ 11. Claro, um problema é como declarar tal função. Cada lambda tem um tipo, mas esse tipo não pode ser expresso em C ++. Eu não acho que isso funcionaria:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

Nem este:

int(int) retFun();

Não estou ciente de nenhuma conversão automática de lambdas para, digamos, ponteiros para funções ou algo parecido. A única solução é criar um objeto de função manualmente e devolvê-lo?

Bartosz Milewski
fonte
1
Para adicionar o que já foi dito, as funções lambda sem estado são conversíveis em ponteiros de função.
snk_kid
3
IMO, sua primeira opção não funcionará, pois o lambda no decltypenão é o mesmo que no corpo da função e, portanto, tem um tipo diferente (mesmo se você incluiu a instrução return)
Motti
1
A propósito, se um lambda tiver uma cláusula de captura vazia, ele pode ser implicitamente conversível em um ponteiro para função.
GManNickG
@GMan: A menos que você esteja usando o Visual C ++ 2010 ou uma versão do g ++ lançada há mais de um ano (ou por aí). A conversão implícita de lambda sem captura em ponteiro de função não foi adicionada até março de 2010 no N3092.
James McNellis
4
Expressões lambda em geral não podem aparecer em operandos não avaliados. Portanto, decltype([](){})or sizeof([]() {})está malformado, não importa onde você o escreva.
Johannes Schaub - litb

Respostas:

98

Você não precisa de um objeto de função artesanal, apenas use std::function, para o qual as funções lambda são conversíveis:

Este exemplo retorna a função de identidade de inteiro:

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}
Sean
fonte
6
Duh! Eu amo StackOverflow. Levaria muito mais tempo para pesquisar o tópico do que obter a resposta no StackOverflow. Obrigado Sean!
Bartosz Milewski de
5
Isso vai causar uma alocação de memória no construtor de std::function.
Maxim Egorushkin
2
@Maxim Yegorushkin std :: function tem semântica de movimento e pode usar alocadores personalizados e o rascunho de trabalho C ++ 0x tem estas notas: "[Nota: as implementações são encorajadas a evitar o uso de memória alocada dinamicamente para pequenos objetos chamáveis, por exemplo , onde o alvo de f é um objeto que contém apenas um ponteiro ou referência a um objeto e um ponteiro de função de membro. - nota final] "então, basicamente, você não pode fazer muitas suposições sobre qual estratégia de alocação uma implementação específica está usando, mas você deve ser capaz para usar seus próprios alocadores (agrupados) de qualquer maneira.
snk_kid
1
@Maxim: Minha resposta foi para a pergunta "A única solução é criar um objeto de função manualmente e devolvê-lo?"
Sean
2
Basta ter em mente que std::functionemprega apagamento de tipo, o que pode significar o custo de fazer uma chamada de função virtual ao chamar o std::function. Algo para estar ciente se a função retornada será usada em um loop interno restrito ou outro contexto em que a pequena ineficiência seja importante.
Anthony Hall
29

Para este exemplo simples, você não precisa std::function.

Do padrão §5.1.2 / 6:

O tipo de fechamento para um lambda-expressão sem lambda-captura tem uma função pública não-virtual não explícita conversão const para ponteiro para função de ter os mesmos tipos de parâmetro e retorno como operador de chamada de função do tipo de fechamento. O valor retornado por esta função de conversão deve obrigatoriamente ser o endereço de uma função que, quando invocada, tem o mesmo efeito que invocar o operador de chamada de função do tipo de encerramento.

Como sua função não tem uma captura, isso significa que o lambda pode ser convertido em um ponteiro para função do tipo int (*)(int):

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

Esse é o meu entendimento, corrija-me se eu estiver errado.

user534498
fonte
1
Isso parece certo. Infelizmente, ele não funciona com o compilador atual que estou usando: VS 2010. A conversão de std :: function funciona.
Bartosz Milewski
3
Sim, a redação final desta regra veio tarde demais para o VC2010.
Ben Voigt
Eu adicionei um exemplo de código. Aqui está um programa completo .
jfs
@JFSebastian - Qual é o tempo de vida do lambda neste exemplo? É longo o suficiente para sobreviver ao resultado da conversão para ponteiro de função?
Flexo
1
@JFSebastian - Não consigo encontrar uma resposta para isso, então fiz uma pergunta em si mesma: stackoverflow.com/questions/8026170/…
Flexo
21

Você pode retornar a função lambda de outra função lambda, já que não deve especificar explicitamente o tipo de retorno da função lambda. Basta escrever algo assim em escopo global:

 auto retFun = []() {
     return [](int x) {return x;};
 };
Dzhioev
fonte
2
Isso só é verdade quando o lambda externo consiste apenas na instrução de retorno. Caso contrário, você deve especificar o tipo de retorno.
Bartosz Milewski
3
Esta é a melhor resposta, pois não requer polimorfismo em tempo de execução de std :: function e permite que lambda tenha uma lista de captura não vazia, no entanto, eu usaria const auto fun = ...
robson3.14
2
@BartoszMilewski não é verdade desde C ++ 14.
dzhioev
19

Embora a questão pergunte especificamente sobre C ++ 11, para o bem de outras pessoas que se deparam com isso e têm acesso a um compilador C ++ 14, C ++ 14 agora permite tipos de retorno deduzidos para funções comuns. Portanto, o exemplo em questão pode ser ajustado apenas para funcionar conforme desejado, simplesmente descartando a -> decltypecláusula ... após a lista de parâmetros de função:

auto retFun()
{
    return [](int x) { return x; }
}

Observe, entretanto, que isso não funcionará se mais de um return <lambda>;aparecer na função. Isso ocorre porque uma restrição na dedução do tipo de retorno é que todas as instruções de retorno devem retornar expressões do mesmo tipo, mas cada objeto lambda recebe seu próprio tipo exclusivo pelo compilador, portanto return <lambda>;, cada expressão terá um tipo diferente.

Anthony Hall
fonte
4
Por que mencionar os tipos deduzidos de c ++ 14, mas omitir lambdas polimórficos? auto retFun() { return [](auto const& x) { return x; }; }
ver
1

Você deve escrever assim:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

para obter seu retorno como uma função e usá-lo como:

int val = returnFunction(someNumber);
Terens Tare
fonte
0

Se você não tiver o c ++ 11 e estiver executando seu código c ++ em micro controladores, por exemplo. Você pode retornar um ponteiro vazio e, em seguida, executar uma conversão.

void* functionThatReturnsLambda()
{
    void(*someMethod)();

    // your lambda
    someMethod = []() {

        // code of lambda

    };

    return someMethod;
}


int main(int argc, char* argv[])
{

    void* myLambdaRaw = functionThatReturnsLambda();

    // cast it
    auto myLambda = (void(*)())myLambdaRaw;

    // execute lambda
    myLambda();
}
Tono Nam
fonte