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

230

En C ++ 11, existe-t-il un moyen de modèle d'une fonction lambda? Ou est-il intrinsèquement trop spécifique pour être modelé?

Je comprends que je peux plutôt définir une classe / un foncteur de modèles classique, mais la question est plus comme: le langage autorise-t-il les modèles de fonctions lambda?

Klaim
la source
Existe-t-il un cas d'utilisation où un modèle lambda serait utile?
James McNellis
7
James: Vous pouvez créer une fonction pour itérer sur un tuple (pas nécessairement utile).
Joe D
J'ai pensé à l'idée en lisant une interview de Stroustrup parlant de la complexité du méta-modèle étant un problème. Si cela était autorisé, j'imaginais le code-ninja fu qui pourrait être inventé par des programmeurs trop intelligents jouant avec cette combinaison de fonctionnalités ...
Klaim

Réponses:

181

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


MISE À JOUR 2014: C ++ 14 est sorti cette année et fournit maintenant aux lambdas polymorphes la même syntaxe que dans cet exemple. Certains grands compilateurs l'implémentent déjà.


À l'heure actuelle (en C ++ 11), malheureusement non. Les lambdas polymorphes seraient excellents en termes de flexibilité et de puissance.

La raison originale pour laquelle ils ont fini par être monomorphes était à cause des concepts. Les concepts ont rendu cette situation de code difficile:

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 contraints. (Sinon, les contraintes ne pourraient pas être vérifiées.) Peut être fooinvoqué bar(x)? Quelles contraintes le lambda a-t-il (le paramètre n'est-il qu'un modèle, après tout)?

Les concepts n'étaient pas prêts à s'attaquer à ce genre de choses; cela nécessiterait plus de choses comme late_check(où le concept n'a pas été vérifié jusqu'à ce qu'il soit invoqué) et d'autres choses. Le plus simple était de tout laisser tomber et de s'en tenir aux lambdas monomorphes.

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

GManNickG
la source
5
Simple ... sauf qu'il y a un désir de réintroduire des concepts et d'éviter les fonctionnalités qui les compliquent.
6
Je pense que je préfère les lambdas polymorphes que les concepts. Je ne comprends pas comment l'exemple motive quoi que ce soit; vous pouvez simplement l'interdire comme une erreur et exiger que le lambda soit monomorphe [] (T x) {} ou un modèle contraint [] modèle <Contrainte T> (T x) {}, qui peut être vérifié statiquement pour correspondre. Y a-t-il une raison pour laquelle cela n'a pas été possible?
DrPizza
13
Vous n'avez pas à choisir entre les concepts et les lambdas polymorphes: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams
3
Voici la proposition pour les lambdas polymorphes: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf et l'implémentation du jouet dans clang: faisalv.github.com/clang-glambda
Radif Sharafullin
18
Les Lambdas polymorphes seront en C ++ 14, au moins ils sont dans le draft de la communauté maintenant :)
Arne Mertz
37

Les lambdas C ++ 11 ne peuvent pas être modélisés comme indiqué dans d'autres réponses, mais decltype()semblent 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);
}

Tirages:

My string
1024
1

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

Joel
la source
26
Tfonctionnerait bien à la place de decltype(t)dans cet exemple.
user2023370
26

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. [La 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 autodéduction de type, mais utilisera plutôt les règles de déduction d'argument de modèle. Voir également la proposition d'expressions lambda génériques (et la mise à jour de celle-ci).

Timo Türschmann
la source
5
Les règles de autodéduction de type sont spécifiquement définies pour être les mêmes que celles de la templatedéduction d'argument de fonction.
underscore_d
10

Je suis conscient que cette question concerne C ++ 11. Cependant, pour ceux qui ont googlé et ont atterri sur cette page, les lambdas basés sur des modèles sont désormais pris en charge en C ++ 14 et portent le nom de Lambdas génériques.

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

RAM
la source
6

Je me demande ce qu'il en est:

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 je me demande si le compilateur optimisera la fonction "wrapping".

ted
la source
2
Quel compilateur? L'a fait?
NicoBerrogorry
4

Jetez un œil à 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, par le façon :)

usta
la source
2
Je le sais déjà, mais la question concerne exactement la nouvelle norme de toute façon;)
Klaim
Ok :) Les lambdas C ++ 0x sont monomorphes et ne peuvent malheureusement pas être modélisés.
usta
3

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

_widgetsest unstd::tuple< fusion::pair<Key_T, Widget_T>... >

user6559931
la source
FWIW, cela est devenu une syntaxe standard en C ++ 20.
LF
2

J'ai joué avec la dernière version 5.0.1compilation de clang avec le -std=c++17drapeau et il y a maintenant un bon support pour les paramètres de type automatique pour les 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;
}
Doug Coburn
la source
1

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();                                                            
   }                                                                    
};   

Pour utiliser do:

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

Le principal problème avec cela (en plus de la saisie supplémentaire), vous ne pouvez pas intégrer 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 ça:

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

Avec l'espoir de pouvoir l'utiliser comme ceci:

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

Mais j'obtiens l'erreur du compilateur:

error: lambda-expression in unevaluated context

Donc, cela ne fonctionne pas ... mais même s'il compilait, cela serait d'une utilité limitée car nous aurions encore à mettre le "using LamdaT" à la portée du fichier (car c'est un modèle) qui défait en quelque sorte le but de lambdas.

rmccabe3701
la source
1

Je ne sais pas pourquoi personne d'autre ne l'a suggéré, mais vous pouvez écrire une fonction de modèle qui renvoie des fonctions lambda. Ce qui suit a résolu mon problème, la raison pour laquelle je suis venu sur 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 f("any string")revient maintenant 1.0.

Voilà un exemple de ce que je veux dire par «fonction lambda basée sur des modèles». (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 leurs données.)

Jim Pivarski
la source
2
Cela ne fonctionne que si vous connaissez le type d'argument du lambda avant de créer le lambda, auquel cas vous pouvez simplement utiliser un lambda avec le type spécifique comme argument. Le point de lambda polymorphe est de fournir un travail à effectuer sur un type d'argument que vous ne connaissez jamais lorsque vous écrivez le code de travail. Fondamentalement, c'est totalement différent, c'est pourquoi cela n'a pas été suggéré.
Klaim
Ah, d'accord, j'ai compris. Je ne pensais pas à ce cas d'utilisation --- je pense aux fonctions lambda comme des choses à la volée et ce genre de polymorphisme comme quelque chose dans une bibliothèque polyvalente. J'écrivais une bibliothèque basée sur des modèles qui doit accepter les fonctions lambda de l'utilisateur de tout type et également fournir les valeurs par défaut du bon type.
Jim Pivarski