2016-03-17 13 views
13

Perché questo codice viene compilato con GCC (4.9 e 5+), ma non con clang (3.5-3.9)?Ancora un'altra discrepanza clang/gcc per quanto riguarda l'uso di ODR?

void test(const int&) { } 
int main() { 
    const int x = 42; 
    auto f = []{ test(x); }; 
} 

ho qualche vaga idea che la discrepanza ha a che fare con ODR (Regola Una definizione) l'utilizzo, ma non capiscono che abbastanza bene per capire che cosa sta succedendo qui.

+1

interessante clang è felice se si effettua x static –

+3

@RichardHodges Le variabili con durata di archiviazione statica non devono essere catturate. – Brian

+0

Qualcun altro pensa che sia un po 'sciocco che uno debba catturare esplicitamente il contesto invece del compilatore che lo ha capito per noi? – BitTickler

risposta

16

x è utilizzato odr perché è associato a un riferimento (parametro test). Essa pertanto deve essere catturato ([expr.prim.lambda]/13):

Se un lambda-espressione o un'istanza di modello operatore chiamata di funzione di un lambda generico ODR-usi ([basic.def.odr]) this oppure una variabile con durata di archiviazione automatica dal suo ambito di raggiungimento, , tale entità deve essere catturata dall'espressione lambda .

Le violazioni di questa regola, come tutte le altre regole del standard che non dice "no necessaria diagnostica" o "un comportamento indefinito", require a diagnostic.

GCC, sfortunatamente, esegue il piegamento costante troppo presto, prima che possa dire se è un odr-use o meno. Ciò può causare problemi such as [&]()->const int & { return x; } returning a dangling reference.

+0

@MM tu potrebbe certamente sfruttare questo per produrre lo stesso effetto, giusto? –

+0

@MM Sono abbastanza sicuro ** ** [class.local] **/1 si applica (poiché un lambda è una classe locale): * Le dichiarazioni in una classe locale non devono odr-use (3.2) una variabile con memorizzazione automatica durata da un ambito che racchiude. * – ecatmur

+0

@ecatmur hai un riferimento per questo? In N4140 il termine "classe locale" non appare nella sezione [expr.prim.lambda].(anche se questo è discutibile visto che TC trova specificatamente una regola equivalente per lambdas) –

8

T.C. ha la giusta diagnosi, ecco un po 'più chiaro del codice legale dove clang fa la cosa giusta e gcc non: indirizzi

#include <iostream> 

void test(const int&a) { std::cout << "in test() -- " << &a << "\n"; } 
int main() { 
    const int x = 42; 
    std::cout << "in main() -- " << &x << "\n"; 
    auto f = [&]{ test(x); }; 
    f(); 
} 

stampe gcc diversi per una variabile di cattura per riferimento rispetto all'originale!

+3

È anche peggio di così; prova 'int main() {const int x = 42; return [&] {const int & a = x; const int & b = x; return & a == & b; }(); } '- gcc lega i due riferimenti a distinti temporali di valore! – ecatmur

+0

@ecatmur: Ci scusiamo per il rumore. È davvero difficile vedere dove finisce il lambda e il main continua di nuovo, per il codice in un commento. Sì, è malvagio. –

Problemi correlati