Pourquoi ne puis-je pas créer un vecteur de lambdas (du même type) en C ++ 11?

88

J'essayais de créer un vecteur de lambda, mais j'ai échoué:

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

Jusqu'à la ligne n ° 2, il compile bien . Mais la ligne n ° 3 donne une erreur de compilation :

erreur: pas de fonction correspondante pour l'appel à 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)'

Je ne veux pas de vecteur de pointeurs de fonction ou de vecteur d'objets de fonction. Cependant, un vecteur d'objets de fonction qui encapsulent des expressions lambda réelles fonctionnerait pour moi. Est-ce possible?

Nawaz
la source
23
"Je ne veux pas de vecteur de pointeurs de fonction ou de vecteur d'objets de fonction." Mais c'est ce que vous avez demandé. Un lambda est un objet fonction.
Nicol Bolas

Réponses:

135

Chaque lambda a un type différent, même s'ils ont la même signature. Vous devez utiliser un conteneur d'encapsulation au moment de l'exécution, par exemple std::functionsi vous souhaitez faire quelque chose comme ça.

par exemple:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });
Chiot
la source
52
Gérer une équipe de développeurs de cent hommes ressemble plus à un cauchemar pour moi :)
Jeremy Friesner
10
N'oubliez pas non plus que les lambdas sans capture ([] -style) peuvent se dégrader en pointeurs de fonction. Ainsi, il pouvait stocker un tableau de pointeurs de fonction du même type. Notez que VC10 ne l'implémente pas encore.
Nicol Bolas
À propos, ne devrait-il pas de toute façon utiliser capture-less dans ces exemples? Ou est-ce nécessaire? - À propos, le pointeur lambda sans capture de fonction semble être pris en charge dans VC11. Je ne l'ai pas testé cependant.
Klaim le
2
Est-il possible de créer un vecteur stockant des fonctions de type différent? c'est-à-dire qu'au lieu de le limiter std::function<int(), pourrais-je utiliser différents prototypes de fonctions?
manatttta le
2
@manatttta Quel serait le point? Des conteneurs existent pour stocker des objets du même type, pour les organiser et les manipuler ensemble. Vous pourriez aussi bien demander "puis-je créer un vectorstockage à la fois std::functionet std::string?" Et la réponse est la même: non, car ce n'est pas l'usage prévu. Vous pouvez utiliser une classe de style `` variante '' pour effectuer suffisamment d'effacement de type pour mettre des éléments disparates dans un conteneur, tout en incluant une méthode permettant à un utilisateur de déterminer le type `` réel '' et donc de choisir quoi faire (par exemple, comment appeler) chaque élément ... mais encore une fois, pourquoi aller si loin? Y a-t-il une réelle justification?
underscore_d
40

Toutes les expressions lambda ont un type différent, même si elles sont identiques caractère par caractère . Vous poussez un lambda d'un type différent (car c'est une autre expression) dans le vecteur, et cela ne fonctionnera évidemment pas.

Une solution consiste à créer un vecteur de std::function<int()>.

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

Sur une autre note, ce n'est pas une bonne idée à utiliser [&]lorsque vous ne capturez rien.

R. Martinho Fernandes
la source
14
Pas besoin ()de lambdas qui ne prennent aucun argument.
Puppy
18

Bien que ce que d'autres ont dit soit pertinent, il est toujours possible de déclarer et d'utiliser un vecteur de lambda, bien que ce ne soit pas très utile:

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

Ainsi, vous pouvez stocker n'importe quel nombre de lambdas là-dedans, à condition qu'il s'agisse d'une copie / déplacement de lambda!

Luc Danton
la source
Ce qui pourrait en fait être utile si le refoulement se produit dans une boucle avec des paramètres différents. Vraisemblablement à des fins d'évaluation paresseuse.
MaHuJa
7
Non, vous ne mettez pas les paramètres dans le vecteur, juste l'objet de fonction .. Donc ce serait un vecteur avec toutes les copies du même lambda
hariseldon78
16

Si votre lambda est sans état, c'est-à-dire, [](...){...}C ++ 11 lui permet de se dégrader en un pointeur de fonction. En théorie, un compilateur compatible C ++ 11 serait capable de compiler ceci:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3
MSN
la source
4
Pour mémoire auto ignore = *[] { return 10; };, ferait ignoreun fichier int(*)().
Luc Danton
1
@Luc, oh c'est dégoûtant! Quand ont-ils ajouté cela?
MSN le
3
Eh bien, puisque la fonction de conversion qui permet de prendre un pointeur de fonction en premier lieu est obligée de ne pas l'être explicit, le déréférencement d'une expression lambda est valide et déréférence le pointeur résultant de la conversion. Puis en utilisant des autodésintégrations de cette référence en un pointeur. (Utilisant auto&ou auto&&aurait gardé la référence.)
Luc Danton
Ah ... Déréférencer le pointeur résultant. Ça a du sens. La disparition était-elle ()intentionnelle ou accidentelle?
MSN
Intentionnelle, l'expression lambda est équivalente (mais deux caractères plus courts).
Luc Danton
6

Vous pouvez utiliser une fonction de génération lambda (mise à jour avec le correctif suggéré par 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++;
    }
}

Mais je pense que vous avez essentiellement créé votre propre cours à ce stade. Sinon, si les lambdas ont des caputres / arguments complètement différents, etc., vous devrez probablement utiliser un tuple.

antédiluvien
la source
Bonne idée de l'envelopper dans une fonction comme lambda_genqui peut être à son tour un lambda en lui-même. Cependant, auto a = lambda_gen(1);effectue un appel inutile, ce qui peut être évité si nous écrivons ceci decltype(lambda_gen(1)).
Nawaz
Cela ne fait-il pas encore un appel supplémentaire? Un autre point mineur est que la question indique C ++ 11, il faudrait donc ajouter un type de retour à la fin de la fonction, je pense.
antédiluvien
Tout ce qui se trouve à l'intérieur decltype n'est pas évalué , donc l'appel n'est pas réellement effectué. C'est le même cas avec sizeof. De plus, ce code ne fonctionnera pas en C ++ 11 même si vous ajoutez un type de retour de fin !!
Nawaz
4

Chaque lambda est d'un type différent. Vous devez utiliser à la std::tupleplace de std::vector.

Paul Fultz II
la source