Le espressioni Lambda come modello di classe i parametri

Può espressioni lambda essere utilizzato come classe i parametri del template? (Nota: questa è una questione diversa rispetto questo, che chiede se un’espressione lambda stesso può essere modellata.)

Mi sto chiedendo se è possibile fare qualcosa di simile:

template <class Functor> 
struct Foo { };
//...
Foo<decltype([]()->void { })> foo;

Questo potrebbe essere utile nei casi in cui, per esempio, un modello di classe ha vari parametri, come la equal_to o qualcosa del genere, che di solito sono attuate come one-liner functors. Per esempio, supponiamo che io voglia creare un’istanza di una tabella hash che usa il mio personalizzati confronto di uguaglianza funzione. Mi piacerebbe essere in grado di dire qualcosa come:

typedef std::unordered_map<
  std::string,
  std::string,
  std::hash<std::string>,
  decltype([](const std::string& s1, const std::string& s2)->bool 
    { /* Custom implementation of equal_to */ })
  > map_type;

Ma ho provato questo su GCC 4.4 e 4.6, e non funziona, a quanto pare perché il tipo anonimo creato da un’espressione lambda non ha un costruttore di default. (Ricordo un problema simile con boost::bind.) C’è qualche motivo il progetto di norma non consente questo, o mi sbaglio e non è consentito, ma il GCC è proprio dietro nella loro attuazione?

InformationsquelleAutor Channel72 | 2011-05-01



4 Replies
  1. 52

    Mi sto chiedendo se è possibile fare qualcosa di simile:

    Foo<decltype([]()->void { })> foo;

    No, non puoi, perché le espressioni lambda non deve comparire un unevaluated contesto (come decltype e sizeof, tra gli altri).
    C++0x FDI, 5.1.2 [expr.prim.lambda] p2

    La valutazione di un lambda-expression risultati in un prvalue temporanea (12.2). Questo temporanea è chiamato
    chiusura in oggetto. Un lambda-expression non deve comparire un unevaluated operando (Clausola 5). [ Nota:
    chiusura in oggetto si comporta come un oggetto di funzione (20.8).—fine nota ]
    (sottolineatura mia)

    È necessario innanzitutto creare una specifica lambda e quindi utilizzare decltype che:

    auto my_comp = [](const std::string& left, const std::string& right) -> bool {
      //whatever
    }
    
    typedef std::unordered_map<
      std::string,
      std::string,
      std::hash<std::string>,
      decltype(my_comp)
      > map_type;

    Che è perché ogni lambda-derivati chiusura oggetto potrebbe avere un tipo completamente diverso, sono come anonimo funzioni, dopo tutto.

    • le “funzioni anonime” non sono realmente funzioni. Questo va detto a voce alta, per questo è fonte di confusione.
    • Aggiunto un po ‘ di “mi piace” per renderlo più chiaro.
    • href=”https://connect.microsoft.com/VisualStudio/feedback/details/636117/broken-lambda” >connect.microsoft.com/VisualStudio/feedback/details/636117/… – Dopo la lettura di questo davvero si chiede perché chiunque, anche si avvicinò con l’idea di chiamarli senza nome fncs? Non sarebbe meglio e in realtà corretta se erano chiamati “ufficialmente” oggetti senza nome? Non ho mai, mai, mai chiamare l’espressione lambda senza nome fnc per la ragione che semplicemente non è un fnc.
    • Senza cattura, sono innominabile funzioni. Con la cattura, sono innominabile functors. L’utilizzo di “fncs” come abbreviazione è davvero male, perché non distinguere funzioni da functors.
    • fnctr e fnc sono molto particolare 😉 ok, ma dove sta dicendo (la cosa che hai detto su di loro di essere questo o quello ;), ti dispiacerebbe “link me” alla fonte?
    • Stesso paragrafo, come menzionato nella risposta, ma p6: “La chiusura per un lambda-expression senza lambda-capture ha un pubblico di non-virtuale non esplicita const funzione di conversione di puntatore a funzione con lo stesso parametro e restituisce i tipi, come la chiusura del tipo operatore di chiamata della funzione. Il valore restituito da questa funzione di conversione deve essere l’indirizzo di una funzione che, quando viene richiamato, ha lo stesso effetto di invocare la chiusura del tipo operatore di chiamata della funzione.”
    • la sezione 5.1.2, commi 1, 2 e 6. p1 e p2 spiega che le espressioni lambda creare oggetti di funzione (functors). p6 dice che non cattura lambda corrisponde a un comune privo di funzione (da poter essere memorizzati in un normale puntatore a funzione).
    • Ben riassunti.
    • Confermato che la soluzione che si darà funziona come previsto in VC2010.
    • si può “corrispondono” a un comune privo di funzione, ma è ancora un oggetto non di una funzione.
    • Il risultato dell’espressione lambda è un oggetto, ma la logica è una normale funzione (piuttosto che una funzione membro). E l’oggetto decadimenti di un puntatore a questa ordinaria funzione.
    • Sono contento che ti dicono che è un oggetto. LE ESPRESSIONI LAMBDA NON SONO ANONIMO FUNZIONI.
    • Ma sono convertibili in unnamed funzioni.
    • e 0 è convertibile in nullptr ma non la chiamerei nullptr si farebbe? E vero è convertibile a 1 e ancora non la chiamerei “letterale” 1 faresti? E solo perché X è divisibile per Y non significa che X è Y. ecco perché per esempio non si mangia a 5 euro o dollari o qualunque sia la valuta è in paese per il pranzo, ma si mangia qualcosa in cui si sono “convertiti”, quei 5 euro.
    • È una funzione non semplicemente un oggetto per cui l’operatore di chiamata della funzione (operator()()) è definita per eseguire il codice associato? Si potrebbe dire che una captureless lambda è la definizione stessa di un innominabile funzione.
    • Una funzione è fondamentalmente sempre un puntatore a funzione, che può anche essere privo di riferimenti (provare (*main)() come esempio). Quello che si ottiene indietro da risolvere è una funzione di designazione (penso che è quello che si chiama) che, immediatamente, e, silenziosamente, viene convertito in un puntatore a funzione (si può fare (********main)().
    • In C, sì. In C++, funzione designatori non sono convertito ai puntatori a funzione se la funzione di operatore di chiamata viene applicato in molti altri contesti implicito di conversione di prendere posto. Il punto è puntatori a funzione sono oggetti. Quindi, solo perché una lambda è un oggetto non significa un captureless lambda non è una funzione. Si nota che captureless lambda oggetti e funzione designatori sia implicitamente decadimento di puntatori a funzione.
    • matrici anche decadere “on demand” per un puntatore e ancora li chiami array non puntatori. Il punto è che la lambda non sono senza nome fncs. Sono oggetti senza nome. E quando hai detto “È una funzione non semplicemente un oggetto per…” posso solo dire una cosa: Non in C++ senso.

  2. 9

    @Xeo le ha dato ragione, quindi io ti do il lavoro intorno.

    Spesso non si desidera il nome di una chiusura, in questo caso, è possibile utilizzare std::function, che è un tipo:

    typedef std::unordered_map<
      std::string,
      std::string,
      std::hash<std::string>,
      std::function<bool(std::string const&, std::string const&)>
      > map_type;

    Nota che coglie esattamente la firma della funzione, e non di più.

    Allora si può semplicemente scrivere la lambda quando costruire la mappa.

    Nota che con unordered_map, se si modifica il confronto di uguaglianza, è meglio modificare l’hash per associare il comportamento. Oggetti uguali, hanno lo stesso hash.

    • Non è utile, std::function non contiene il operator() comportamento che l’attuale lambda tipo.
    • Come è diverso? Ho pensato std::function era basato su boost::funzione che si comporta allo stesso, non è vero?
    • Il comportamento è contenuto in un particolare esempio di function. Ma il modello è utilizzando solo il tipo, non esempio, e quindi non c’è problema. Hashtable attuazione è andando a creare una nuova istanza ogni volta che si vuole richiamare il funtore, e function è astratto — non può essere istanziata.
    • un default costruito function getta std::bad_function_call se richiamato (praticamente inutile). Che cosa significa in che sopra di fornire il tipo utilizzato per l’archiviazione, è necessario fornire una funzione di confronto esempio durante la creazione di un’istanza di map_type; per esempio una lambda. Si noti che il confronto in oggetto è costruito dopo, non ad ogni invocazione. Può essere stateful se gli chiedi di essere e la loro funzione è non astratto. Davvero. Vedi doc.
    • Sì, c’è una mancata corrispondenza tra la progettazione in questa domanda, e l’uso corretto della std::function. Anche se si potrebbe progettare una hashtable che utilizza std::function, sarebbe un design totalmente diverso da quello che Channel72 richiesto.
    • che è il motivo per cui io sto parlando di un lavoro in giro, piuttosto che una soluzione, dato che non corrisponde esattamente le esigenze.
    • Destra. Il mio commento era per spiegare a Giuseppe, che ha chiesto su questo oggi, come indicato dal @Giuseppe detto in commento.

  3. 5

    Non si può fare questo con una chiusura, perché lo stato non è contenuta nel tipo.

    Se la lambda è stateless (senza cattura), quindi dovrebbe essere ok. In questo caso la lambda decadimenti di un normale puntatore a funzione, che è possibile utilizzare come modello argomento invece di alcuni lambda tipo.

    gcc non piace però. http://ideone.com/bHM3n

    • Utilizzando la soluzione che Xeo inviato, è può utilizzare cattura… si avrebbe bisogno solo di passare il tuo lambda per il unordered_map nel costruttore (perché non in grado di costruire una stessa).
    • Non riesco a vedere niente in Xeo la risposta che passa un lambda oggetto intorno. La lambda oggetto viene creato con l’unico scopo di passare a decltype. Che non vuol dire che è più complesso di utilizzo non è possibile, ma penso che avrai problemi con la necessità della lambda sia in ambito globale per nome il modello di istanza, che non preclude l’acquisizione. Si può usare una combinazione di std::function come suggerito da @DeadMG e un lambda oggetto specificato in fase di runtime, ma poi si sacrificare l’efficienza della fase di compilazione polimorfismo.
    • no, non è possibile utilizzare la soluzione essere pubblicato Xeo per questo. Cercando dà un errore come questo: error: use of deleted function ‘<lambda(int, int)>::<lambda>()’.
    • Da qui il mio ‘passare la tua lambda … non si può costruire una stessa” qualificatore. Si dovrebbe chiamare la forma lunga del unordered_map costruttore, che consente il passaggio di un esempio di lambda come ultimo parametro.
    • Demo: ideone.com/yq8zBs
    • sì… purtroppo non tutte le classi del modello hanno una forma lunga costruttore. 🙁
    • Quando questa risposta è stata scritta, tipi locali non erano ammessi come modello di argomenti. Ecco perché ho già commentato “che necessitano la lambda sia in ambito globale per nome il modello di istanza”. La restrizione finalmente fatto ottenere sollevato in un nuovo standard C++, rendendo tutto questo possibile.

  4. 0

    Si dovrà utilizzare un run-time di tipo astratto, come std::function, o creare il tipo di una variabile locale o come parte di una classe basata su modelli.

    • Credo che l’obiettivo sia per il tipo per esprimere il comportamento, e std::function esprime solo la firma.

Lascia un commento