2016-01-08 22 views
56

In [except.ctor] lo standard (N4140) garantisce che:I valori di ritorno delle funzioni sono oggetti automatici e quindi sono garantiti per essere distrutti?

... distruttori vengono invocati per tutti gli oggetti automatici costruiti dal momento che il blocco try è stato immesso ...

Tuttavia, nell'esempio seguente, lo output vuoto dimostra che il valore restituito della funzione foo non è stato destrutturato, sebbene sia stato costruito. Compilato usando g ++ (5.2.1) e clang ++ (3.6.2-1) e con le opzioni -O0 -fno-elide-constructors -std=c++14.

struct A { ~A() { cout << "~A\n"; } }; 

struct B { ~B() noexcept(false) { throw 0; } }; 

A foo() { 
    B b; 
    return {}; 
} 

int main() { 
    try { foo(); } 
    catch (...) { } 
} 

È questo un bug sia in g ++ e clang ++, o sono i valori di ritorno delle funzioni non considerati oggetti automatici, o è una scappatoia nel linguaggio C++?

In nessuno dei [stmt.return], [expr.call] o [dcl.fct] Sono stato in grado di trovare una dichiarazione chiara se un valore di ritorno della funzione è considerato un oggetto automatica . I suggerimenti che ho trovato più vicini sono 6.3.3 p2:

... Una dichiarazione di ritorno può prevede la costruzione e copiare o spostare di un oggetto temporaneo ...

e 5.2.2 p10 :

una chiamata di funzione è un lvalue se il tipo risultato è un tipo di riferimento lvalue o un riferimento rvalue operato tipo, un xValue se il tipo di risultato è un riferimento rvalue al tipo di oggetto, ed una prvalue altrimenti .

risposta

45

I valori di ritorno delle funzioni sono considerati temporanei e la costruzione del valore di ritorno viene sequenziata prima della distruzione dei locali.

Sfortunatamente, questo è sottodimensionato nello standard. C'è un open defect che descrive questo ed offre alcune formulazioni per risolvere il problema

[...] Una dichiarazione di ritorno con un operando di tipo void deve essere utilizzato solo in una funzione il cui tipo di ritorno è cv vuoto. Una dichiarazione di reso con qualsiasi altro operando deve essere utilizzata solo in una funzione il cui tipo di reso non è cv vuoto; la dichiarazione di ritorno inizializza l'oggetto o il riferimento da restituire da copy-initialization (8.5 [dcl.init]) dall'operando. [...]

L'inizializzazione della copia dell'entità restituita viene sequenziata prima della distruzione dei provvisori alla fine dell'espressione completa stabilita dall'operando dell'istruzione return, che, a sua volta, viene sequenziata prima della distruzione delle variabili locali (6.6 [stmt.jump]) del blocco che racchiude l'istruzione return.

Poiché i valori di ritorno delle funzioni sono temporanei, non sono inclusi nella citazione destructors are invoked for all automatic objects all'inizio del post. Tuttavia, [class.temporary]/3 dice:

[...] gli oggetti temporanei vengono distrutti come ultimo passo nella valutazione del full-espressione che (lessicalmente) contiene il punto in cui sono stati creati. Questo è vero anche se la valutazione termina con il lancio di un'eccezione. [...]

Quindi penso che si possa considerare questo un errore in GCC e Clang.

Non gettare da distruttori;)

+6

Ho scoperto che sia gcc che clang hanno già registrato questo errore da anni, quindi non mi aspetto che lo risolvano presto: [gcc] (https://gcc.gnu.org/bugzilla/show_bug.cgi ? id = 33799), [clang] (https://llvm.org/bugs/show_bug.cgi?id=12286). –

+0

Il compilatore è autorizzato ad omettere la stessa costruzione dell'oggetto 'A' in questo caso? Intendo alcune regole simili a RVO. – Mikhail

+0

@Mikhail Non ci credo. RVO non può eliminare completamente la costruzione, può eliminare le costruzioni * intermedie *. – TartanLlama

7

Questo è un bug e, per una volta, MSVC ha effettivamente ragione: stampa "~ A".

+0

vedere alcune volte clang e gcc respinge il codice, ma MSVC accetta il codice con comportamento definito. È successo anche a me una volta, se richiesto, posso fornire un esempio. –

+6

Potresti aggiungere un riferimento allo standard che provi che un valore di ritorno di una funzione è considerato un oggetto automatico o che prova altrimenti che si tratta di un bug in gcc e clang? –

+0

@FlorianKaufmann Penso che [questo esempio] (http://coliru.stacked-crooked.com/a/ee1a5d76230ec21c) mostri che c'è da qualche parte un oggetto 'A' per il quale il distruttore non viene mai chiamato, anche se esce dallo scope dopo essere stato creato. – Antonio

7

Ho modificato il codice e penso che ora dall'output possiamo vedere che A non viene distrutto.

#include<iostream> 

using namespace std; 

struct A { 
    ~A() { cout << "~A\n"; } 
    A() { cout << "A()"; } 
}; 

struct B { 
    ~B() noexcept(false) { cout << "~B\n"; throw(0); } 
    B() { cout << "B()"; } 
}; 

A foo() { 
    B b; 
    return; 
} 

int main() { 
    try { foo(); } 
    catch (...) {} 
} 

e l'uscita è:

B() A() ~ B

Quindi sì potrebbe essere un bug.

Problemi correlati