Por que não consigo criar um vetor de lambdas (do mesmo tipo) em C ++ 11?

87

Eu estava tentando criar um vetor de lambda, mas falhou:

auto ignore = [&]() { return 10; };  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]() { return 100; });  //3

Até a linha 2, ele compila perfeitamente . Mas a linha 3 fornece erro de compilação :

erro: nenhuma função correspondente para chamada para 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

Não quero um vetor de ponteiros de função ou vetor de objetos de função. No entanto, o vetor de objetos de função que encapsulam expressões lambda reais funcionaria para mim. Isso é possível?

Nawaz
fonte
23
"Não quero um vetor de ponteiros de função ou vetor de objetos de função." Mas foi isso que você pediu. Um lambda é um objeto de função.
Nicol Bolas

Respostas:

134

Cada lambda tem um tipo diferente - mesmo se eles tiverem a mesma assinatura. Você deve usar um contêiner de encapsulamento de tempo de execução, como std::functionse você quiser fazer algo assim.

por exemplo:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });
Cachorro
fonte
52
Gerenciar uma equipe de desenvolvedores de cem homens parece mais um pesadelo para mim :)
Jeremy Friesner
10
Além disso, não se esqueça de que lambdas sem capturas (estilo []) podem degradar em ponteiros de função. Portanto, ele poderia armazenar uma matriz de ponteiros de função do mesmo tipo. Observe que o VC10 ainda não implementa isso.
Nicol Bolas
A propósito, capture-less não deveria ser usado nesses exemplos de qualquer maneira? Ou é necessário? - A propósito, lambda sem captura para ponteiro de função parece ser suportado no VC11. Mas não testei.
Klaim de
2
É possível criar um vetor armazenando funções de tipo diferente? ou seja, em vez de limitar a std::function<int(), posso usar diferentes protótipos de função?
manatttta
2
@manatttta Qual seria o ponto? Os contêineres existem para armazenar objetos do mesmo tipo, para organizá-los e manipulá-los juntos. Você também pode perguntar 'posso criar um vectorarmazenamento de ambos std::functione std::string?' E a resposta é a mesma: Não, porque esse não é o uso pretendido. Você pode usar uma classe de estilo 'variante' para realizar o apagamento de tipo suficiente para colocar coisas diferentes em um contêiner, enquanto inclui um método para um usuário determinar o tipo 'real' e, portanto, escolher o que fazer (por exemplo, como chamar) cada elemento ... mas, novamente, por que ir tão longe? Existe alguma razão real?
sublinhado_d
40

Todas as expressões lambda têm um tipo diferente, mesmo se forem idênticas caractere por caractere . Você está colocando um lambda de um tipo diferente (porque é outra expressão) no vetor e isso obviamente não funcionará.

Uma solução é criar um vetor de std::function<int()>.

auto ignore = [&]() { return 10; };
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]() { return 100; });

Por outro lado, não é uma boa ideia usar [&]quando você não está capturando nada.

R. Martinho Fernandes
fonte
14
Não precisa ()de lambdas que não aceitam argumentos.
Filhote de cachorro de
18

Embora o que outros disseram seja relevante, ainda é possível declarar e usar um vetor de lambda, embora não seja muito útil:

auto lambda = [] { return 10; };
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

Portanto, você pode armazenar qualquer número de lambdas lá, contanto que seja uma cópia / movimentação de lambda!

Luc Danton
fonte
O que pode realmente ser útil se o pushback acontecer em um loop com parâmetros diferentes. Presumivelmente para fins de avaliação preguiçosos.
MaHuJa
7
Não, você não coloca os parâmetros no vetor, apenas o objeto de função .. Então seria um vetor com todas as cópias do mesmo lambda
hariseldon78
16

Se seu lambda não tem estado, ou seja, [](...){...}C ++ 11 permite que ele se transforme em um ponteiro de função. Em teoria, um compilador compatível com C ++ 11 seria capaz de compilar isso:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3
MSN
fonte
4
Para o registro auto ignore = *[] { return 10; };faria ignoreum int(*)().
Luc Danton
1
@Luc, isso é nojento! Quando eles adicionaram isso?
MSN
3
Bem, como a função de conversão que permite obter um ponteiro de função em primeiro lugar é obrigada a não ser explicit, a desreferenciação de uma expressão lambda é válida e desreferencia a referência do ponteiro resultante da conversão. Em seguida, o uso de autotransforma essa referência em um ponteiro. (Usando auto&ou auto&&teria mantido a referência.)
Luc Danton
Ah ... Desreferenciando o ponteiro resultante. Isso faz sentido. A falta foi ()intencional ou acidental?
MSN
Intencionalmente, a expressão lambda é equivalente (mas dois caracteres a menos).
Luc Danton de
6

Você pode usar uma função de geração de lambda (atualizada com a correção sugerida por Nawaz):

#include <vector>
#include <iostream>

int main() {
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec){
        std::cout << lambda(i) << std::endl;
        i++;
    }
}

Mas acho que você basicamente fez sua própria aula neste momento. Caso contrário, se os lambdas tiverem caputres / args completamente diferentes, etc., você provavelmente terá que usar uma tupla.

antediluviano
fonte
Boa ideia envolvê-lo em uma função como a lambda_genque pode ser, por sua vez, um lambda. No entanto, auto a = lambda_gen(1);faz uma chamada desnecessária, que pode ser evitada se escrevermos isso decltype(lambda_gen(1)).
Nawaz
Isso ainda não faz uma chamada extra? Outro ponto secundário é que a questão indica C ++ 11, portanto, seria necessário adicionar um tipo de retorno à direita para a função, eu acho.
antediluviano
Não. Qualquer coisa interna nãodecltype é avaliada , então a chamada não é realmente feita. É o mesmo caso com sizeoftambém. Além disso, este código não funcionará em C ++ 11, mesmo se você adicionar o tipo de retorno final !!
Nawaz
4

Cada lambda é um tipo diferente. Você deve usar em std::tuplevez de std::vector.

Paul Fultz II
fonte