Qual è il tipo di lambda quando dedotto con "auto" in C ++ 11?


142

Ho avuto la percezione che il tipo di lambda sia un puntatore a funzione. Quando ho eseguito il test seguente, ho scoperto che era sbagliato ( demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

Nel codice sopra manca qualche punto? In caso contrario, qual è l' typeofespressione a lambda se dedotta con la autoparola chiave?


8
"Type of a lambda è un puntatore a funzione" - sarebbe inefficiente e perderebbe l'intero punto di lambda.
Konrad Rudolph,

Risposte:


145

Il tipo di un'espressione lambda non è specificato.

Ma sono generalmente semplici zuccheri sintattici per i maestri. Una lambda viene tradotta direttamente in un funzione. Qualsiasi cosa all'interno di []viene trasformata in parametri del costruttore e membri dell'oggetto functor, mentre i parametri all'interno ()vengono trasformati in parametri per il functor operator().

Un lambda che non cattura variabili (nulla all'interno di []) può essere convertito in un puntatore a funzione (MSVC2010 non supporta questo, se quello è il tuo compilatore, ma questa conversione fa parte dello standard).

Ma il tipo effettivo di lambda non è un puntatore a funzione. È un tipo di funzione non specificato.


1
MSVC2010 non supporta la conversione in puntatore a funzione, ma MSVC11 lo supporta. blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

18
+1 per "semplice zucchero sintattico per funzione". Molta potenziale confusione può essere evitata ricordando questo.
Ben

4
un funtore è nulla con operator()fondamentalmente stackoverflow.com/questions/356950/c-functors-and-their-uses
TankorSmash

107

È una struttura unica senza nome che sovraccarica l'operatore di chiamata di funzione. Ogni istanza di un lambda introduce un nuovo tipo.

Nel caso speciale di un lambda non catturante, la struttura inoltre ha una conversione implicita in un puntatore a funzione.


2
Bella risposta. Molto più preciso del mio. +1 :)
jalf

4
+1 per la parte dell'unicità, all'inizio è molto sorprendente e merita attenzione.
Matthieu M.

Non che sia davvero importante, ma il tipo è davvero senza nome, o non gli viene dato un nome fino al momento della compilazione? IOW, si potrebbe usare RTTI per trovare il nome deciso dal compilatore?
Ben

3
@Ben, è senza nome e per quanto riguarda il linguaggio C ++, non esiste una cosa come "un nome su cui il compilatore decide". Il risultato di type_info::name()è definito dall'implementazione, quindi potrebbe restituire qualsiasi cosa. In pratica, il compilatore nominerà il tipo per il bene del linker.
avakar,

1
Ultimamente, quando mi viene posta questa domanda, di solito dico che il tipo di lambda ha un nome, il compilatore lo sa, è semplicemente indicibile.
Andre Kostur,

24

[C++11: 5.1.2/3]: Il tipo dell'espressione lambda (che è anche il tipo dell'oggetto di chiusura) è un tipo di classe non sindacale unico, senza nome, chiamato tipo di chiusura , le cui proprietà sono descritte di seguito. Questo tipo di classe non è un aggregato (8.5.1). Il tipo di chiusura è dichiarato nell'ambito di blocco, ambito di classe o ambito di spazio dei nomi più piccolo che contiene l' espressione lambda corrispondente . [..]

La clausola continua con l'elenco delle diverse proprietà di questo tipo. Ecco alcuni punti salienti:

[C++11: 5.1.2/5]:Il tipo di chiusura per una lambda espressione ha un pubblico inlineoperatore chiamata di funzione (13.5.4) i cui parametri e tipo di ritorno sono descritti dalla lambda-espressione ‘s parametro dichiarazione clausola e trascinamento-ritorno-tipo rispettivamente. [..]

[C++11: 5.1.2/6]:Il tipo di chiusura per un'espressione lambda senza acquisizione lambda ha una funzione di conversione const non esplicita pubblica non virtuale in puntatore a funzione con gli stessi parametri e tipi restituiti dell'operatore di chiamata della funzione del tipo di chiusura. Il valore restituito da questa funzione di conversione deve essere l'indirizzo di una funzione che, se richiamata, ha lo stesso effetto di invocare l'operatore di chiamata della funzione del tipo di chiusura.

La conseguenza di questo passaggio finale è che, se si è utilizzato una conversione, si sarebbe in grado di assegnare LAMBDAa pFptr.


3
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

I tipi di funzione sono effettivamente gli stessi, ma lambda introduce un nuovo tipo (come un funzione).


Raccomando l'approccio districante di CXXABI se stai già seguendo questa strada. Invece, di solito uso __PRETTY_FUNCTION__, come in template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }, e spoglio il extra se inizia a diventare affollato. Preferisco vedere i passaggi mostrati nella sostituzione del modello. Se ti manca __PRETTY_FUNCTION__, ci sono alternative per MSVC, ecc., Ma i risultati dipendono sempre dal compilatore per lo stesso motivo per cui CXXABI è necessario.
John P,

1

Dovrebbe anche notare che lambda è convertibile in puntatore a funzione. Tuttavia typeid <> restituisce un oggetto non trvial che dovrebbe differire da lambda al puntatore a funzione generica. Quindi il test per typeid <> non è un presupposto valido. In generale C ++ 11 non vuole che ci preoccupiamo delle specifiche del tipo, tutto ciò che conta se un determinato tipo è convertibile in un tipo di destinazione.


È giusto, ma i tipi di stampa fanno molto per arrivare al tipo corretto, per non parlare dei casi in cui il tipo è convertibile ma non soddisfa altri vincoli. (Spingerei sempre verso "reificare" i vincoli ove possibile, ma qualcuno che cerca di farlo ha molte più ragioni per mostrare il proprio lavoro durante lo sviluppo.)
John P

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.