web-dev-qa-db-fra.com

Le "type" d'une expression lambda peut-il être exprimé?

En considérant les expressions lambda comme un «sucre syntaxique» pour les objets appelables, le type sous-jacent non nommé peut-il être exprimé? 

Un exemple: 

struct gt {
    bool operator() (int l, int r) {
        return l > r;
    }
} ;

Maintenant, [](int l, int r) { return l > r; } est un remplacement élégant pour le code ci-dessus (plus la création nécessaire d'objets appelables de gt), mais existe-t-il un moyen d'exprimer gt (le type) lui-même? 

Un usage simple: 

std::set<int, gt> s1;  // A reversed-order std::set
// Is there a way to do the same using a lambda?
std::set<int, some-magic-here-maybe([](int l, int r) { return l > r; }) > s2;
51

Non, vous ne pouvez pas le mettre dans decltype car

Une expression lambda ne doit pas apparaître dans un opérande non évalué

Vous pouvez faire ce qui suit si

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> s(n);

Mais c'est vraiment moche. Notez que chaque expression lambda crée un nouveau type unique. Si vous faites ensuite ce qui suit ailleurs, t a un type différent de s

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> t(n);

Vous pouvez utiliser std::function ici, mais notez que cela engendrera un coût d'exécution minime, car il nécessite un appel indirect à l'opérateur d'appel d'objet de fonction lambda. C'est probablement négligeable ici, mais cela peut être important si vous voulez transmettre les objets de fonction de cette manière à std::sort par exemple.

std::set<int, function<bool(int, int)>> s([](int l, int r) { return l > r; });

Comme toujours, d'abord le code puis le profil :)

58

Réponse directe à votre question: non.

Vous devrez utiliser quelque chose qui soit assignable à partir de tout type ressemblant à un foncteur ayant un type bien défini. Un exemple est std :: function comme indiqué dans la réponse de sbi. Ce n'est cependant pas le type de l'expression lambda.

1
Crazy Eddie

Au moins dans Microsoft Visual Studio (je n'ai pas essayé cela avec d'autres compilateurs) et si vous ne capturez rien, le type semble être un pointeur de fonction normal:

std::string (*myFunctionPointer)(int x) = [] (int x) {
  char buffer[10];
  return std::string("Test ") + itoa(x, buffer, 10);
};
std::string testOutput = myFunctionPointer(123);
0

Vous pouvez utiliser une petite classe lambda_wrapper <>, pour envelopper un lambda à bas prix. C'est beaucoup plus rapide que std :: function car il n'y a pas d'appel de fonction virtuelle et une allocation de mémoire dynamique . Wrapper fonctionne en déduisant la liste d'arguments lambda et le type de retour.

#include <iostream>
#include <functional>
#include <set>

template <typename T, typename ... Args>
struct lambda_wrapper : public lambda_wrapper<decltype(&T::operator())(Args...)> {};

template <typename L>
struct lambda_wrapper<L> {
private:
    L lambda;

public:
    lambda_wrapper(const L & obj) : lambda(obj) {}

    template<typename... Args>
    typename std::result_of<L(Args...)>::type operator()(Args... a) {
        return this->lambda.operator()(std::forward<Args>(a)...);
    }

    template<typename... Args> typename
    std::result_of<const L(Args...)>::type operator()(Args... a) const {
        return this->lambda.operator()(std::forward<Args>(a)...);
    }
};
template <typename T>
auto make_lambda_wrapper(T&&t) {
    return lambda_wrapper<T>(std::forward<T>(t));
}
int main(int argc, char ** argv) 
{
    auto func = make_lambda_wrapper([](int y, int x) -> bool { return x>y; });
    std::set<int, decltype(func)> ss(func);
    std::cout << func(2, 4) << std::endl;
}
0
barney