Perché non posso creare un vettore di lambda (dello stesso tipo) in C ++ 11?


88

Stavo cercando di creare un vettore di lambda, ma non è riuscito:

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

Fino alla riga # 2, si compila bene . Ma la riga # 3 dà un errore di compilazione :

errore: nessuna funzione corrispondente per la chiamata a 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

Non voglio un vettore di puntatori a funzione o un vettore di oggetti funzione. Tuttavia, il vettore di oggetti funzione che incapsulano espressioni lambda reali , funzionerebbe per me. È possibile?


23
"Non voglio un vettore di puntatori a funzione o un vettore di oggetti funzione." Ma è quello che hai chiesto. Un lambda è un oggetto funzione.
Nicol Bolas

Risposte:


135

Ogni lambda ha un tipo diverso, anche se ha la stessa firma. È necessario utilizzare un contenitore incapsulante in fase di esecuzione, ad esempio std::functionse si desidera eseguire un'operazione del genere.

per esempio:

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

52
Gestire un team di sviluppatori
composto da

10
Inoltre, non dimenticare che i lambda senza captur (stile []) possono degradarsi in puntatori a funzione. Quindi poteva memorizzare un array di puntatori a funzione dello stesso tipo. Nota che VC10 non lo implementa ancora.
Nicol Bolas

A proposito, in quegli esempi non dovrebbe essere usato comunque senza cattura? O è necessario? - A proposito, lambda senza captur al puntatore a funzione sembra essere supportato in VC11. Non l'ho provato però.
Klaim

2
È possibile creare un vettore che memorizzi funzioni di diverso tipo? cioè invece di limitarlo a std::function<int(), potrei usare diversi prototipi di funzioni?
manatttta

2
@manatttta Quale sarebbe il punto? Esistono contenitori per memorizzare oggetti dello stesso tipo, per organizzarli e manipolarli insieme. Potresti anche chiedere "posso creare un vectorarchivio sia std::functione std::string?" E la risposta è la stessa: no, perché non è questo l'uso previsto. Potresti usare una classe in stile 'variant' per eseguire una cancellazione del tipo sufficiente per mettere cose disparate in un contenitore, includendo un metodo per un utente per determinare il tipo 'reale' e quindi scegliere cosa fare (ad esempio come chiamare) ogni elemento ... ma ancora, perché andare a tanto? C'è una vera logica?
underscore_d

40

Tutte le espressioni lambda hanno un tipo diverso, anche se sono identiche carattere per carattere . Stai inserendo un lambda di un tipo diverso (perché è un'altra espressione) nel vettore e ovviamente non funzionerà.

Una soluzione è invece creare un vettore std::function<int()>.

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

In un'altra nota, non è una buona idea usare [&]quando non stai catturando nulla.


14
Non sono necessari ()lambda che non accettano argomenti.
Puppy

18

Sebbene ciò che altri hanno detto sia rilevante, è ancora possibile dichiarare e utilizzare un vettore di lambda, sebbene non sia molto utile:

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

Quindi, puoi memorizzare un numero qualsiasi di lambda lì, purché sia ​​una copia / mossa di lambda!


Che potrebbe effettivamente essere utile se il pushback avviene in un ciclo con parametri diversi. Presumibilmente per scopi di valutazione pigri.
MaHuJa

7
No, non metti i parametri nel vettore, solo l'oggetto funzione .. Quindi sarebbe un vettore con tutte le copie dello stesso lambda
hariseldon78

16

Se il tuo lambda è senza stato, cioè, [](...){...}C ++ 11 gli consente di degradarsi in un puntatore a funzione. In teoria, un compilatore conforme a C ++ 11 sarebbe in grado di compilare questo:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3

4
Per la cronaca auto ignore = *[] { return 10; };farei ignoreun file int(*)().
Luc Danton

1
@ Luc, oh che è disgustoso! Quando l'hanno aggiunto?
MSN

3
Bene, poiché la funzione di conversione che consente di prendere un puntatore a funzione in primo luogo è obbligata a non esserlo explicit, la dereferenziazione di un'espressione lambda è valida e dereferenzia il puntatore risultante dalla conversione. Quindi utilizzare autodecadimenti che rimandano a un puntatore. (Utilizzando auto&o auto&&avrebbe mantenuto il riferimento.)
Luc Danton,

Ah ... Dereferenziare il puntatore risultante. Ha senso. La scomparsa è stata ()intenzionale o accidentale?
MSN

Intenzionale, l'espressione lambda è equivalente (ma due caratteri più corti).
Luc Danton

6

È possibile utilizzare una funzione di generazione lambda (aggiornata con la correzione suggerita da 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++;
    }
}

Ma penso che tu abbia praticamente creato la tua lezione a questo punto. Altrimenti se i lambda hanno caputres / args ecc. Completamente diversi, probabilmente dovrai usare una tupla.


Bella l'idea di avvolgerlo in una funzione come quella lambda_genche può essere a sua volta una lambda stessa. Tuttavia, auto a = lambda_gen(1);fa una chiamata non necessaria, che può essere evitata se scriviamo questo decltype(lambda_gen(1)).
Nawaz

Ma questo non fa ancora una chiamata in più? Anche un altro punto minore è che la domanda afferma C ++ 11 quindi sarebbe necessario aggiungere un tipo di ritorno finale alla funzione, penso.
antidiluviano

No. Qualunque cosa all'interno non decltype è valutata , quindi la chiamata non viene effettivamente effettuata. È lo stesso caso anche con sizeof. Inoltre, questo codice non funzionerà in C ++ 11 anche se aggiungi il tipo di ritorno finale !!
Nawaz

4

Ogni lambda è di un tipo diverso. Devi usare std::tupleinvece di std::vector.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.