2015-04-10 14 views
14

ho un quadro che definisce eccezione come classe noncopyable, da cui abbiamo derivato una classe copiabile (che definisce un costruttore di copia chiamando un non-copia costruttore della classe base)Lanciare copiabile classe derivante dalla noncopyable

questo funziona sotto g ++, ma non sotto MSVC 2013.

il seguente codice riprodurre il problema:

#include <iostream> 

using namespace std; 

#if defined _MSC_VER 
#define __PRETTY_FUNCTION__ __FUNCTION__ 
#endif 

class u { 
    u(const u&) = delete; 
    const u& operator=(const u&) = delete;/* the library we use defines it as const u& */ 
public: 
    u() { cout << __PRETTY_FUNCTION__ << "def" << endl; } 
protected: 
    explicit u(int i) { cout << __PRETTY_FUNCTION__ << "int: " << i << endl; } 
}; 

class e : public u { 
public: 
    e() { cout << __PRETTY_FUNCTION__ << "def" << endl; } 
    e(const e& _e) : u(1) { cout << __PRETTY_FUNCTION__ << "cpy" << endl; } 
    e& operator=(const e& _e) { cout << __PRETTY_FUNCTION__ << endl; return *this; } 
}; 

int foo() { 
    e _e; 
    throw _e; 

    return 0; 
} 

int main() { 
    try { 
    foo(); 
    } catch(const e& _e) { 
    cout << "in catch e" << endl; 
    } catch(...) { 
    cout << "in catch..." << endl; 
    } 
#if defined _MSC_VER 
    cout << "press enter to exit" << endl; 
    cin.get(); 
#endif 
    return 0; 
} 

MSVC lamenta Error 1 error C2280: 'u::u(const u &)' : attempting to reference a deleted function alla fine della funzione foo().

g ++ e clang entrambi compilano il codice e non usano affatto il costruttore di copie (l'oggetto e viene spostato), ma nessuno dei due verrà compilato se e non è costruibile con la copia.

MODIFICA: ho modificato il codice per forzare una copia.

BTW, se le funzioni di copia u non vengono eliminate (né definite, pre-C++ 11 non copiabili), MSVC non riesce nella fase di collegamento durante la ricerca u::u(const u&). (esterno non risolto)

C'è un difetto nel mio codice o questo errore è presente in MSVC?

+4

Questo funziona su http://webcompiler.cloudapp.net/, quindi ho intenzione di andare con un bug MSVC che è stato corretto in MSVC 2015. –

+0

sembra un errore msvc, non richiamare il costruttore di movimento: http: // stackoverflow.com/questions/21715468/can-i-throw-a-unique-ptr – benlong

+0

La mia prima ipotesi sarebbe che quando si lancia un oggetto 'e', si potrebbe prenderlo come' u'. (Non come riferimento ad un 'u'.) In tal caso avresti bisogno del copy ctor di' u'. Forse gcc e clang mettono quell'invocazione di copia vicino alla clausola catch (che non esiste qui), ma MSVC potrebbe volerlo mettere dopo l'istruzione 'throw'. Non sarebbe invocato, ma il compilatore lo fa riferimento. –

risposta

-1

MSVC 2013 richiede davvero classi di eccezioni copiabili. Inoltre, il suo costruttore deve essere esplicito. Il seguente codice è corretto per MSVC 2013:

class Ex { 
public: 
    Ex(){} 
private: 
    explicit Ex(const Ex&); 
}; 

int main() 
{ 
    throw Ex(); // error 
} 

Sembra che non sarete in grado di gestire questo problema in modo corretto; qualsiasi magia con i puntatori porterà alla cancellazione di oggetti impropri.

Vedo solo una soluzione: notificare gli sviluppatori del framework del problema.

+0

Questo codice viene compilato su VS2013 –

+1

Spiacente, ero ubriaco) Ho risolto il mio esempio – vaselo

1

In primo luogo, e e u entrambi hanno costruttori di copia dichiarati dall'utente. Ciò significa che non hanno un operatore di costruzione del movimento o di assegnazione del movimento generato in modo implicito. (Di conseguenza, il tuo reclamo "L'oggetto e è stato spostato" è falso; più su questo sotto).

e è copiabile, u non è copiabile. e è considerato mobile perché lo spostamento ricade alla copia se non esiste un costruttore di movimento.


Secondo sezione [except.throw] di C++ 14 (era lo stesso in C++ 11), l'inizializzazione di oggetti quando si lancia l'eccezione è equivalente a:

e temp = e_;  // (1) 
const e& _e = temp; // (2) 

Per (1), non viene creato temporaneo, perché e_ e temp hanno lo stesso tipo.

Per (2) è direttamente vincolante: _e si riferisce a temp direttamente e non c'è di nuovo temporaneo.


Si può ricordare che (1) è un contesto copy elision. Ciò significa che deve esistere una copia accessibile o un costruttore di mosse; ma il compilatore può saltare la chiamata a quel costruttore e usare lo stesso spazio di memoria per entrambi gli oggetti.

Questo è probabilmente ciò che intendevi dicendo "L'oggetto e viene spostato": hai visto che non è stato copiato, ma si presume una mossa quando in realtà era copia elisione.


Mettere tutto insieme: l'unico requisito per la throw - catch di lavorare è che e ha una mossa costruttore callable o costruttore di copia. (Questo costruttore potrebbe non essere effettivamente chiamato, ma deve esistere).

Il tuo e dispone infatti di un costruttore di copia richiamabile, quindi il codice è corretto e la versione di MSVC che hai provato era protetta da bug.

Se si ha ancora accesso a tale versione, sarebbe interessante provare effettivamente a scrivere e temp = e_; const e& _e = temp; e vedere se quel codice genera lo stesso messaggio di errore. Ciò mostrerebbe se il bug è con l'implementazione del compilatore di inizializzazione della copia, al contrario di essere un bug con il suo lancio.

+0

Hai ragione per spostare/copiare elision –

+0

Ho ancora accesso a questa versione del compilatore. Il 'e temp = e_; const e & _e = temp; 'il codice si compila senza errori. Il tentativo di lanciare una di queste variabili non riesce. –

Problemi correlati