35 votes

Avant la déclaration de lambdas en C++

En C++, il est possible de séparer la déclaration et la définition de fonctions. Par exemple, il est tout à fait normal de déclarer une fonction:

int Foo(int x);

en Foo.h et le mettre en oeuvre Foo.cpp. Est-il possible de faire quelque chose de similaire avec les lambdas? Par exemple, définir un

std::function<int(int)> bar;

en bar.h et le mettre en oeuvre bar.cpp comme:

std::function<int(int)> bar = [](int n)
{
    if (n >= 5) 
        return n;
    return n*(n + 1);
};

Avertissement: j'ai de l'expérience avec les lambdas en C#, mais je ne les ai pas utilisé en C++ très bien.

44voto

songyuanyao Points 2265

Vous ne pouvez pas séparer déclaration et la définition des lambdas, ni en avant de la déclarer. Son type est un sans nom fermeture type qui est déclarée avec l'expression lambda. Mais vous pouvez le faire avec std::function objets, qui est conçu pour être capable de stocker tout appelable cible, y compris les expressions lambda.

Comme votre exemple de code montre que vous avez été en utilisant std::function, il suffit de noter que, pour ce cas - bar une variable globale, en effet, et vous devez utiliser extern dans le fichier d'en-tête pour faire une déclaration (pas une définition).

// bar.h
extern std::function<int(int)> bar;     // declaration

et

// bar.cpp
std::function<int(int)> bar = [](int n) // definition
{
    if (n >= 5) return n;
    return n*(n + 1);
};

Notez encore une fois que ce n'est pas séparée de la déclaration et la définition de lambda; Il est juste de séparer déclaration et la définition d'une variable globale bar type std::function<int(int)>, ce qui est initialisé à partir d'une expression lambda.

9voto

bashrc Points 701

Strictement parlant, vous ne pouvez pas

Citant la référence du rpc

L'expression lambda est un prvalue expression dont la valeur est (jusqu' C++17)dont l'objet résultat est (depuis C++17) d'un objet temporaire sans nom de unique sans nom non-union de non-agrégation type de classe, connue comme la fermeture le type, qui est déclarée (pour les fins de l'ADL) dans le plus petit le bloc de portée, portée de classe, ou de l'espace de champ qui contient le lambda expression

Ainsi, le lambda est un sans nom temporaire de l'objet. Vous pouvez lier le lambda d'un l-objet de valeur(par exemple std::function) et par les règles ordinaires sur la déclaration de la variable, vous pouvez séparer la déclaration et la définition.

9voto

6502 Points 42700

Avant la déclaration n'est pas le bon terme, parce que les lambdas en C++ sont des objets et non des fonctions. Le code:

std::function<int(int)> bar;

déclare une variable et que vous n'êtes pas forcé de l'affecter (ce type a une valeur par défaut de "pointeur de fonction"). Vous pouvez compiler appelle même à elle... par exemple le code:

#include <functional>
#include <iostream>

int main(int argc, const char *argv[]) {
    std::function<int(int)> bar;
    std::cout << bar(21) << "\n";
    return 0;
}

compiler proprement (mais, bien sûr, va se comporter follement à l'exécution).

Cela dit, vous pouvez attribuer un lambda compatibles std::function variable et en ajoutant par exemple:

bar = [](int x){ return x*2; };

juste avant l'appel à la raison dans un programme qui compile fine et génère en sortie 42.

Un peu de non-choses évidentes qui peuvent être surprenants sur les lambdas en C++ (si vous connaissez d'autres langues que ce concept) qui sont

  • Chaque lambda [..](...){...} a un autre type incompatible, même si la signature est absolument identique. Vous par exemple ne peut pas déclarer un paramètre de type lambda, parce que le seul moyen serait d'utiliser quelque chose comme decltype([] ...) mais alors il n'y aurait aucun moyen d'appeler la fonction comme tout autre []... formulaire sur un site d'appel seraient incompatibles. Ce problème est résolu par std::function donc, si vous devez passer des lambdas de contourner ou de les stocker dans des conteneurs, vous devez utiliser std::function.

  • Les Lambdas peuvent capturer les habitants en valeur (mais ils sont const , sauf si vous déclarez le lambda mutable) ou par référence (mais la garantie de la durée de vie de l'objet référencé ne sera pas plus courte que la durée de vie de la lambda est au programmeur). C++ n'a pas de garbage collector et c'est quelque chose de nécessaire pour résoudre correctement la "la hausse funarg" problème (vous pouvez contourner par la capture des pointeurs intelligents, mais vous devez faire attention pour les boucles de référence afin d'éviter les fuites).

  • Différemment des autres langues lambdas peuvent être copiés et lorsque vous les copiez vous prenez un instantané de leur capturé par les variables de la valeur. Cela peut être très surprenant pour mutable état et c'est je crois la raison pour laquelle capturé par valeur, les valeurs sont const par défaut.

Un moyen de rationaliser et n'oubliez pas beaucoup de détails sur les lambdas, c'est que le code comme:

std::function<int(int)> timesK(int k) {
    return [k](int x){ return x*k; };
}

c'est un peu comme

std::function<int(int)> timesK(int k) {
    struct __Lambda6502 {
        int k;
        __Lambda6502(int k) : k(k) {}
        int operator()(int x) {
            return x * k;
        }
    };
    return __Lambda6502(k);
}

avec une différence subtile que même lambda capturer les références peuvent être copiés (normalement classes contenant des références en tant que membres ne peuvent pas).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X