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?


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; };
}

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.


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;};
 };

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.


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);

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();
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.