web-dev-qa-db-fra.com

Les fonctions lambda peuvent-elles être modélisées?

En C++ 11, existe-t-il un moyen de modéliser une fonction lambda? Ou est-ce trop spécifique pour être modélisé?

Je comprends que je peux définir une classe/un foncteur classique basé sur des modèles, mais la question se pose plutôt: le langage permet-il de modéliser des fonctions lambda?

207
Klaim

MISE À JOUR 2018: C++ 20 viendra avec des lambdas modélisés et conceptualisés. La fonctionnalité a déjà été intégrée dans le projet standard.


MISE À JOUR 2014: Les versions C++ 14 ont été publiées cette année et fournissent maintenant les lambda polymorphes avec la même syntaxe que dans cet exemple. Certains compilateurs majeurs l'implémentent déjà.


À cela se tient (en C++ 11), malheureusement non. Les lambdas polymorphes seraient excellents en termes de flexibilité et de puissance.

La raison initiale pour laquelle ils ont fini par être monomorphes était due à des concepts. Les concepts ont rendu difficile la situation de ce code:

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

Dans un modèle contraint, vous ne pouvez appeler que d'autres modèles soumis à des contraintes. (Sinon, les contraintes ne pourraient pas être vérifiées.) Est-ce que foo peut invoquer bar(x)? Quelles sont les contraintes du lambda (son paramètre n’est qu’un modèle, après tout)?

Les concepts n'étaient pas prêts à s'attaquer à ce genre de chose; cela demanderait plus de choses comme late_check (où le concept n'était pas vérifié avant d'être invoqué) et d'autres choses. Plus simple était simplement de tout laisser tomber et de s'en tenir aux lambdas monomorphes.

Cependant, avec la suppression des concepts de C++ 0x, les lambda polymorphes redeviennent une proposition simple. Cependant, je ne trouve aucune proposition à ce sujet. :(

164
GManNickG

Les lambdas C++ 11 ne peuvent pas être modélisés comme indiqué dans d'autres réponses, mais decltype() semble aider lors de l'utilisation d'un lambda dans une classe ou une fonction modélisée.

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

Impressions:

My string
1024
1

J'ai trouvé cette technique utile lorsque vous travaillez avec du code basé sur un modèle, mais réalisez que cela signifie que les lambdas eux-mêmes ne peuvent pas être basés sur un modèle.

36
Joel

En C++ 11, les fonctions lambda ne peuvent pas être modélisées, mais dans la prochaine version de la norme ISO C++ (souvent appelée C++ 14), cette fonctionnalité sera introduite. [Source]

Exemple d'utilisation:

auto get_container_size = [] (auto container) { return container.size(); };

Notez que bien que la syntaxe utilise le mot-clé auto, la déduction de type n'utilisera pas les règles de déduction de type auto, mais utilisera les règles de déduction d'argument de modèle. Voir aussi le proposition d’expressions lambda génériques (et le pdate ).

23
Timo Türschmann

Je suis conscient que cette question concerne C++ 11. Cependant, pour ceux qui ont googlé et atterri sur cette page, les lambda basés sur un modèle sont maintenant supportés en C++ 14 et portent le nom Generic Lambdas.

[info] La plupart des compilateurs populaires prennent en charge cette fonctionnalité maintenant. Microsoft Visual Studio 2015 prend en charge. Clang prend en charge. GCC prend en charge.

9
Ram

Je me demande quoi à propos de ça:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

J'ai utilisé un code similaire, comme celui-ci, pour générer un modèle et me demander si le compilateur optimisera la fonction "wrapping".

6
ted

Regardez Boost.Phoenix pour les lambdas polymorphes: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html Ne nécessite pas C++ 0x, au fait :)

4
usta

Il existe une extension gcc qui autorise les modèles lambda :

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

_widgets est un std::Tuple< fusion::pair<Key_T, Widget_T>... >

3
user6559931

J'ai joué avec le dernier clang version 5.0.1 compiler avec le -std=c++17 flag et il existe maintenant un support Nice pour les paramètres de type automatique pour lambdas:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}
1
Doug Coburn

Voici une solution qui consiste à envelopper le lamba dans une structure:

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

À utiliser faire:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

Le problème principal avec cela (en plus du typage supplémentaire) vous ne pouvez pas incorporer cette définition de structure dans une autre méthode ou vous obtenez (gcc 4.9)

error: a template declaration cannot appear at block scope

J'ai aussi essayé de faire ceci:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

En espérant pouvoir l'utiliser comme ceci:

LamdaT<int>();      
LamdaT<char>();

Mais j'obtiens l'erreur du compilateur:

error: lambda-expression in unevaluated context

Donc ça ne marche pas ... mais même si ça compilait ça aurait une utilité limitée car il faudrait tout de même placer "using LamdaT" dans la portée du fichier (parce que c'est un modèle) lambdas.

1
rmccabe3701

Je ne suis pas sûr de savoir pourquoi personne d'autre ne l'a suggéré, mais vous pouvez écrire une fonction basée sur un modèle qui renvoie des fonctions lambda. Ce qui suit a résolu mon problème, la raison pour laquelle je suis venu à cette page:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

Maintenant, chaque fois que je veux une fonction qui prend un type d’argument donné (par exemple, std::string), Je dis simplement

auto f = makeUnweighted<std::string>()

et maintenant f("any string") renvoie 1.0.

C'est un exemple de ce que je veux dire par "fonction lambda basée sur un modèle". (Ce cas particulier est utilisé pour fournir automatiquement une fonction de pondération inerte lorsque quelqu'un ne veut pas pondérer ses données, quelles que soient ses données.)

1
Jim Pivarski