2016-01-08 14 views
6

Ho il seguente codice:Perché ottengo un errore di deduzione tipo per un lambda ritorno lambda con percorsi di ritorno multipli?

int main() { 
    auto f = [] { 
     if (1) return [] { return 1; }; 
     else return [] { return 2; }; 
    }; 
    return f()(); 
} 

che solleva il seguente errore del compilatore GCC 5.2.1 utilizzando:

error: inconsistent types ‘main()::<lambda()>::<lambda()>’ and 
     ‘main()::<lambda()>::<lambda()>’ deduced for lambda 
     return type else return [] { return 2; }; 

Ora, ovviamente, questi due tipi sembrano essere lo stesso, quindi non sono sicuro se questo è GCC con un messaggio di errore fuorviante o se in realtà è un bug. Secondo la mia conoscenza questo dovrebbe essere compilato; il tipo di ritorno lambda deve essere dedotto di essere std::function<int()>. È interessante notare che se pre-dichiaro il ritorno lambda e restituisco la stessa variabile due volte, funziona.

chiunque può fare luce su ciò che sta accadendo? Ho trovato domande simili, molte attribuite a bug di GCC, ma questo sembra diverso.

+0

hanno diversi tipi: https://ideone.com/Eo4OXW –

+0

Grazie per l'editing la mia domanda a formattare meglio il codice! Non sono sicuro del motivo per cui ho ricevuto un voto negativo. Sarei felice di mettere più "sforzi di ricerca" se necessario. Ho sentito che si trattava di un errore del compilatore piuttosto curioso, quindi speravo che le persone più competenti qui avrebbero saputo cosa stava succedendo. Per favore, indicami tutte le risorse che dovrei usare se questo non è costruttivo. – Alex

+0

Non ho davvero capito i tipi di lambda, mi dispiace per quello. Il messaggio di errore non aiuta, comunque ... – Alex

risposta

13

Ora, ovviamente, questi due tipi sono gli stessi,

No, non lo sono. Il tipo di ogni espressione lambda è un tipo unico e distinto.

Da [expr.prim.lambda]/3:

Il tipo di lambda-espressione (che è anche il tipo dell'oggetto chiusura) è una classe unica, senza nome mancata unione tipo - chiamato il tipo chiusura - le cui proprietà sono descritte di seguito.

Pertanto, tipo di ritorno detrazione per ffallisce e non si traduca in std::function<int()>. Quest'ultimo è un tipo di libreria non correlato che non è in qualche modo magicamente il "tipo comune" di qualsiasi tipo di chiusura.

Naturalmente ognuno dei tipi di chiusura unico può essere convertito-std::function<int()>, quindi se si fornisce il tipo di ritorno, tutto funziona:

auto f = []() -> std::function<int()> { 
    return 1 ? []() { return 1; } 
      : []() { return 2; }; 
}; 

Oppure, come una funzione semplice:

std::function<int()> f() { 
    return 1 ? []() { return 1; } 
      : []() { return 2; }; 
} 
+0

Mi riferivo alle stringhe nel messaggio del compilatore, che sono identiche. Suppongo che il motivo per cui Lambda abbia tutti un tipo unico è una domanda completamente diversa. Ho usato lambda per un po 'di tempo senza sapere questo e quindi ho pensato che questo dovrebbe essere compilato. Grazie per avermi indicato lo standard. – Alex

+1

@Alex: Sono d'accordo, è un messaggio diagnostico terribile. –

+0

@Alex: Non penso che "il motivo per cui lambda ha tipi unici" è una domanda. Ecco come sono definiti. Immagino quale sarebbe una vera domanda "perché la diagnostica GCC non mostra tipi distinti per diverse espressioni lambda". Vale la pena chiedere questo, forse sul bug tracker di GCC. –

2

Ogni lambda ha un suo tipo unico:

Th espressione e lambda crea un oggetto temporaneo prvalue senza nome unico tipo anonimo mancata unione non aggregata [...].

Da here, sottolineare il mio.

Inoltre, lambda non hanno nulla a che fare con std::function, che è un altro tipo diverso.In particolare,

[] { return 1; } 

e

[] { return 2; } 

hanno tipi differenti. Questo è il motivo per cui la deduzione fallisce.

1

Questi tipi sono non lo stesso. Ogni lambda è un'istanza di un tipo unico, senza nome. std::function è un modello che può essere convertito implicitamente da oggetti callable appropriati compresi lambda, ma nessuna istanziazione di std::function è di tipo o tipo padre di qualsiasi lambda, quindi non può essere dedotta.

Tuttavia, si può dire al compilatore che si desidera per tornare std::function e funzionerà:

auto f = []() -> std::function<int()> { 
    if (1) return [] { return 1; }; 
    else return [] { return 2; }; 
} 
+1

Giusto per essere sicuro, 'std :: function' è un modello, non un tipo. Non esiste un solo tipo che può essere convertito da * any * callable. Per ogni tipo di funzione esiste una specializzazione corrispondente di 'std :: function' che può essere convertita da callables di una firma compatibile. –

+0

@KerrekSB È più rilassato, puoi avere 'std :: function' con una firma diversa dal callable fintanto che il compilatore può convertire implicitamente i tipi o riempire i parametri predefiniti, e c'è la strana gestione degli oggetti per i metodi dei membri . Le regole sono piuttosto complesse e ho cercato di evitarli completamente. – StenSoft

+0

Sì, è per questo che ho cercato di aggirarlo dicendo "compatibile". Ma in generale la specializzazione di 'std :: function' deve corrispondere a ciò che il callable sta fornendo in qualche modo; non esiste un tipo di catch-all completamente arbitrario. –

Problemi correlati