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;
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 :)
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.
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);
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;
}