Le funzioni lambda possono essere modellate?


230

In C ++ 11, c'è un modo per modellare una funzione lambda? O è intrinsecamente troppo specifico per essere modellato?

Capisco che posso invece definire una classica classe / funzione basata su modelli, ma la domanda è più simile: il linguaggio consente le funzioni lambda di modello?


Esiste un caso d'uso in cui un modello lambda sarebbe utile?
James McNellis,

7
James: Potresti creare una funzione per iterare su una tupla (non necessariamente utile).
Joe D,

Ho pensato all'idea mentre leggevo un'intervista di Stroustrup che parlava della complessità dei meta-template come un problema. Se fosse permesso, stavo immaginando il codice-fu ninja che potrebbe essere inventato da programmatori troppo intelligenti che giocano con questa combinazione di caratteristiche ...
Klaim

Risposte:


181

AGGIORNAMENTO 2018: C ++ 20 arriverà con lambda templated e concettualizzate. La funzione è già stata integrata nella bozza standard.


AGGIORNAMENTO 2014: C ++ 14 è stato rilasciato quest'anno e ora fornisce agli lambda polimorfici la stessa sintassi di questo esempio. Alcuni importanti compilatori lo implementano già.


Allo stand (in C ++ 11), purtroppo no. Le lambda polimorfiche sarebbero eccellenti in termini di flessibilità e potenza.

La ragione originale per cui finirono per essere monomorfi era a causa di concetti. I concetti hanno reso difficile questa situazione del codice:

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

In un modello vincolato è possibile chiamare solo altri modelli vincolati. (Altrimenti non è possibile verificare i vincoli.) È possibile fooinvocare bar(x)? Quali vincoli ha la lambda (il parametro per questo è solo un modello, dopo tutto)?

I concetti non erano pronti ad affrontare questo genere di cose; richiederebbe più cose come late_check(dove il concetto non è stato verificato fino a quando non è stato invocato) e cose. Semplice è stato semplicemente lasciar perdere tutto e attenersi ai lambda monomorfi.

Tuttavia, con la rimozione di concetti dal C ++ 0x, i lambda polimorfici diventano di nuovo una semplice proposta. Tuttavia, non riesco a trovare alcuna proposta per questo. :(


5
Semplice ... tranne che c'è il desiderio di reintrodurre concetti ed evitare caratteristiche che li rendono complicati.

6
Penso che preferirei lambda polimorfi piuttosto che concetti. Non capisco come l'esempio motiva qualcosa; potresti semplicemente vietarlo come errore e richiedere che lambda sia monomorfo [] (T x) {} o un modello vincolato [] modello <Vincolo T> (T x) {}, che può essere verificato staticamente per corrispondere. C'è qualche motivo per cui questo non è stato possibile?
DrPizza,

13
Non devi scegliere tra concetti e lambda polimorfici: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams,

3
Ecco la proposta di lambda polimorfi: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf e l'implementazione del giocattolo in clang: faisalv.github.com/clang-glambda
Radif Sharafullin

18
Polymorphic Lambdas sarà in C ++ 14, almeno sono già nel Draft della Comunità :)
Arne Mertz,

37

Il lambda C ++ 11 non può essere modellato come indicato in altre risposte, ma decltype()sembra essere utile quando si utilizza un lambda all'interno di una classe o funzione templata.

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

stampe:

My string
1024
1

Ho scoperto che questa tecnica è utile quando si lavora con il codice basato su modelli, ma rendersi conto che significa che gli stessi lambda non possono essere modificati.


26
Tfunzionerebbe bene al posto di decltype(t)in questo esempio.
user2023370

26

In C ++ 11, le funzioni lambda non possono essere modellate, ma nella prossima versione dello standard ISO C ++ (spesso chiamato C ++ 14), questa funzionalità verrà introdotta.[Fonte]

Esempio di utilizzo:

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

Si noti che sebbene la sintassi utilizzi la parola chiave auto, la detrazione del tipo non utilizzerà le regole della autodetrazione del tipo, ma utilizzerà invece le regole della detrazione dell'argomento del modello. Vedi anche la proposta di espressioni lambda generiche (e l' aggiornamento a questa).


5
Le regole della autodetrazione del tipo sono definite in modo specifico per essere le stesse della templatedetrazione dell'argomento della funzione.
underscore_d

10

Sono consapevole che questa domanda riguarda C ++ 11. Tuttavia, per coloro che hanno cercato su Google e approdato su questa pagina, i lambda con template sono ora supportati in C ++ 14 e si chiamano Generic Lambdas.

[info] La maggior parte dei compilatori popolari supporta questa funzione ora. Supporta Microsoft Visual Studio 2015. Clang supporta. GCC supporta.


6

Mi chiedo cosa ne pensi di questo:

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

Ho usato un codice simile come questo, per generare un modello e mi chiedo se il compilatore ottimizzerà la funzione "wrapping".


2
Quale compilatore? Fatto?
NicoBerrogorry,


3

C'è un'estensione gcc che consente i modelli 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);
                          }
              );

dov'è _widgetsastd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW, questa è diventata la sintassi standard in C ++ 20.
LF

2

Ho giocato con l'ultimo clang version 5.0.1compilando con la -std=c++17bandiera e ora c'è un bel supporto per i parametri di tipo automatico per 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

Ecco una soluzione che prevede il wrapping della lamba in una struttura:

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

      lam();                                                            
   }                                                                    
};   

Per usare do:

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

Il problema principale con questo (oltre alla digitazione extra) non è possibile incorporare questa definizione di struttura in un altro metodo o si ottiene (gcc 4.9)

error: a template declaration cannot appear at block scope

Ho anche provato a fare questo:

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

Con la speranza di poterlo usare in questo modo:

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

Ma ottengo l'errore del compilatore:

error: lambda-expression in unevaluated context

Quindi questo non funziona ... ma anche se fosse compilato sarebbe di utilità limitata perché dovremmo ancora mettere "l'utilizzo di LamdaT" nell'ambito del file (perché è un modello) che in qualche modo sconfigge lo scopo di lambda.


1

Non sono sicuro del motivo per cui nessun altro ha suggerito questo, ma puoi scrivere una funzione basata su modelli che restituisce funzioni lambda. Di seguito ho risolto il mio problema, il motivo per cui sono arrivato a questa pagina:

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

Ora ogni volta che voglio una funzione che accetta un determinato tipo di argomento (ad esempio std::string), dico solo

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

e ora f("any string")ritorna 1.0.

Questo è un esempio di ciò che intendo per "funzione lambda basata su modelli". (Questo caso particolare viene utilizzato per fornire automaticamente una funzione di ponderazione inerte quando qualcuno non vuole ponderare i propri dati, qualunque essi siano.)


2
Funziona solo se conosci il tipo di argomento per la lambda prima di creare la lambda, nel qual caso puoi semplicemente usare una lambda con il tipo specifico come argomento. Il punto di lambda polimorfico è fornire lavoro da svolgere su un tipo di argomento che non si conosce mai quando si scrive il codice di lavoro. Fondamentalmente, questo è totalmente diverso, motivo per cui non è stato suggerito.
Klaim,

Ah, giusto, capito. Non ho pensato a quel caso d'uso --- penso alle funzioni lambda come cose al volo e quel tipo di polimorfismo come qualcosa in una biblioteca multiuso. Stavo scrivendo una libreria basata su modelli che deve accettare le funzioni lambda dell'utente di qualsiasi tipo e fornire anche impostazioni predefinite del tipo giusto.
Jim Pivarski,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.