Come funziona lambda generico in C ++ 14?


114

Come funziona lambda generico ( autoparola chiave come tipo di argomento) nello standard C ++ 14?

È basato su modelli C ++ in cui per ogni diverso tipo di argomento il compilatore genera una nuova funzione con lo stesso corpo ma tipi sostituiti (polimorfismo in fase di compilazione) o è più simile ai generici di Java (cancellazione del tipo)?

Esempio di codice:

auto glambda = [](auto a) { return a; };

6
Risolto il problema con C ++ 14, originariamente utilizzato C ++ 11 in questione
sasha.sochka

Risposte:


130

I lambda generici sono stati introdotti in C++14.

Semplicemente, il tipo di chiusura definito dall'espressione lambda avrà un operatore di chiamata basato su modelli piuttosto che il normale operatore di chiamata non modello di C++11lambda di s (ovviamente, quando autoappare almeno una volta nell'elenco dei parametri).

Quindi il tuo esempio:

auto glambda = [] (auto a) { return a; };

Creerà glambdaun'istanza di questo tipo:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

Il paragrafo 5.1.2 / 5 del C ++ 14 Standard Draft n3690 specifica come viene definito l'operatore di chiamata del tipo di chiusura di una data espressione lambda:

Il tipo di chiusura per un'espressione lambda non generica ha un operatore di chiamata di funzione inline pubblico (13.5.4) i cui parametri e il tipo restituito sono descritti rispettivamente dalla clausola di dichiarazione del parametro dell'espressione lambda e dal tipo di ritorno finale. Per un lambda generico, il tipo di chiusura ha un modello di membro dell'operatore di chiamata di funzione inline pubblica (14.5.2) il cui elenco di parametri di modello consiste in un parametro di modello di tipo inventato per ogni occorrenza di auto nella clausola di dichiarazione dei parametri di lambda, in ordine di apparizione. Il tipo di modello-parametro inventato è un pacchetto di parametri se la corrispondente dichiarazione di parametro dichiara un pacchetto di parametri di funzione (8.3.5). Il tipo di ritorno ei parametri di funzione del modello di operatore di chiamata di funzione derivano dal tipo di ritorno finale dell'espressione lambda e dalla clausola di dichiarazione del parametro sostituendo ogni occorrenza di auto negli specificatori di decl della clausola di dichiarazione di parametro con il nome di il corrispondente modello-parametro inventato.

Finalmente:

È simile ai modelli in cui per ogni diverso tipo di argomento il compilatore genera funzioni con lo stesso corpo ma tipi modificati o è più simile ai generici di Java?

Come spiega il paragrafo precedente, i lambda generici sono solo zucchero sintattico per funtori unici e senza nome con un operatore di chiamata basato su modelli. Questo dovrebbe rispondere alla tua domanda :)


7
Tuttavia, consentono anche una classe definita localmente con un metodo modello. Che è nuovo.
Yakk - Adam Nevraumont

2
@Yakk: La restrizione per i modelli locali di funzione non è stata già eliminata del tutto con C ++ 11?
Sebastian Mach

2
@phresnel: No, quella restrizione non è stata revocata
Andy Prowl

1
@ AndyProwl: mi rendo conto del mio errore. Ciò che è stato revocato in effetti è stato l'utilizzo di tipi locali come argomenti modello (come in int main () { struct X {}; std::vector<X> x; })
Sebastian Mach

1
@phresnel: Esatto, questo è davvero cambiato
Andy Prowl

25

Sfortunatamente , non fanno parte di C ++ 11 ( http://ideone.com/NsqYuq ):

auto glambda = [](auto a) { return a; };

int main() {}

Con g ++ 4.7:

prog.cpp:1:24: error: parameter declared auto
...

Tuttavia , il modo in cui potrebbe essere implementato in C ++ 14 secondo la proposta di Portland per lambda generici :

[](const& x, & y){ return x + y; }

Questo produrrebbe per la maggior parte la solita creazione di una classe funtore anonima, ma con la mancanza di tipi il compilatore emetterebbe un membro basato su modelli operator():

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

O secondo la proposta più recente Proposta per espressioni Lambda generiche (polimorfiche)

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

Quindi sì, per ogni permutazione di parametri, sorgerebbe una nuova istanza, tuttavia, i membri di quel funtore sarebbero ancora condivisi (cioè gli argomenti catturati).


6
Quella proposta di consentire l'eliminazione dell'identificatore del tipo è assolutamente grottesca.
Gare di leggerezza in orbita il

Sono entrati con g ++ - 4.9 . Hai bisogno di fornire -std=c++1y.
emsr

Non mi ero reso conto che ideone non avesse ancora gcc-4.9 e C ++ 14.
emsr

domanda: questo autoha le stesse regole di detrazione della classica auto? Se ci riferiamo all'analogia basata su modelli, significherebbe che l'auto non è auto, sono le stesse regole della deduzione del tipo di modello. Quindi la domanda è: la detrazione del modello è equivalente a auto?
v.oddou

@ v.oddou: "Classic auto" va bene. Per me, "classic auto" significa "Stack Variable", e una volta era usato in contrasto con statico register:) Comunque, sì, usare autolì significa che sotto il cofano, viene generato un modello normale. In effetti, un lambda verrà sostituito internamente al compilatore da una classe funtore e un autoparametro significa che template <T> ... (T ...)verrà emesso.
Sebastian Mach

17

È una funzionalità C ++ 14 proposta (non in C ++ 11) simile (o addirittura equivalente) ai modelli. Ad esempio, N3559 fornisce questo esempio:

Ad esempio, questa dichiarazione contenente un'espressione lambda generica:

auto L = [](const auto& x, auto& y){ return x + y; };

potrebbe comportare la creazione di un tipo di chiusura e un oggetto che si comporta in modo simile alla struttura seguente:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
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.