2015-11-25 29 views
6

Ho un problema con i messaggi di errore fuorviante, quando provo a compilare il seguente esempio minimale in Visual Studio 2015:C2280: il tentativo di fare riferimento a una funzione eliminata (unione, struct, copiare costruttore)

class Vector 
{ 
    float x; 
    float y; 

public: 

    Vector(float x, float y) : x(x), y(y) {} 
    Vector& operator = (const Vector& v) { x = v.x; y = v.y; return *this; } 
    //Vector(Vector&&) = default; 
}; 


class Rect 
{ 
public: 
    union { 
     struct { 
      Vector p1, p2; 
     }; 

     struct { 
      float p1x, p1y, p2x, p2y; 
     }; 
    }; 

    Rect() : p1(0,0), p2(0,0) {} 
    Rect(Vector& p1, Vector& p2) : p1(p1), p2(p2) {} 

    /*Rect(const Rect&) = default; 
    Rect& operator=(const Rect&) = default; 
    Rect& operator=(Rect&&) = default; 
    Rect(Rect&&) = default;*/ 
}; 


int main() 
{ 
    Rect test = Rect(); 
    test = Rect(); 
    return 0; 
} 

I ottenuto i seguenti messaggi di errore:

1> ... main.cpp (56): l'errore C2280: 'Rect Rect & :: operator = (const Rect &)': il tentativo di fare riferimento a una funzione eliminata

1> ... main.cpp (50): Nota: compilatore ha generato 'Rett :: operator =' qui

Il compilatore cerca di raccontare me che, il costruttore di copia della classe Rect è una funzione eliminata . Così ho provato ad aggiungere tutti i tipi di altri costruttori (copia) e operatori di assegnazione, come mostrato di seguito, ma senza successo:

Rect(const Rect&) = default; 
Rect& operator=(const Rect&) = default; 
Rect& operator=(Rect&&) = default; 
Rect(Rect&&) = default; 

ho riconosciuto che l'errore in realtà non è causato nella classe Rect. Quando ho commentare la linea

Vector& operator = (const Vector& v) { x = v.x; y = v.y; return *this; } 

le disappiers errore e quando voglio mantenere questa linea, devo aggiungere la seguente riga:

Vector(Vector&&) = default; 

Tuttavia, questo problema sembra presentarsi solo se io sto usando i sindacati e le strutture all'interno della mia classe Rect. Quindi non so, dove il mio errore è effettivamente causato o se solo il messaggio di errore punta alla classe sbagliata.

+2

Si sta utilizzando [Unrestricted_unions] (https://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions) (che non è banale). – Jarod42

+0

Non ho accesso a VS2015. Ma, se si definisce un operatore di assegnazione della copia non predefinito per 'Rect()', il programma si compila usando 'clang ++' ma non con 'g ++'. Esempi: [clang ++] (https://goo.gl/dCcIJc) e [g ++] (http://ideone.com/tFUnFt). Funziona con VS2015? – crayzeewulf

+0

grazie per le vostre risposte Sì, penso che il problema fosse l'utilizzo degli operatori di copia e assegnazione predefiniti. Forse c'è un problema a generarli automaticamente quando si utilizza questo costrutto di unione (senza vincoli). Quando li sostituisco con operatori di copia e assegnazione non predefiniti, non ci sono problemi. –

risposta

1

L'errore proviene dall'unione ponendo l'utilizzo della memoria dei float p1x, ... p2y in cima all'allocazione per gli oggetti Vector.

g ++ fornirebbe un messaggio di errore più esplicito che informa che un oggetto con un costruttore non può essere utilizzato in un unione.

Sono sorpreso che VS non segnala un errore sull'uso di un oggetto direttamente in un sindacato. Sarebbe interessante vedere cosa succede se dichiari i Vettori DOPO i galleggianti nella tua unione.

+0

Gli oggetti con costruttore * possono * essere utilizzati in un unione C'è un esempio in [class.union]/3 di C++ 14. Immagino tu ti riferisca alla regola che se l'oggetto ha un costruttore * non banale, allora il sindacato (o la classe che contiene il sindacato se è un'unione anonima) deve avere un costruttore fornito dall'utente. –

3

Ripetendo il messaggio di errore:

main.cpp(56): error C2280: 'Rect &Rect::operator =(const Rect &)': attempting to reference a deleted function

Questo è abbastanza chiaro: la funzione membro operator= con il parametro const Rect & è stata delete d, ma il codice tenta di chiamare sulla linea test = Rect(); .

È poi dire:

Il compilatore cerca di dirmi che, il costruttore di copia della classe Rect è una funzione eliminata

Tuttavia, è frainteso l'errore. L'errore riguarda la funzione operator =, che viene chiamata operatore di assegnazione copia.Questa è una funzione diversa da copy constructor, che assomiglierebbe a Rect::Rect(const Rect &).


Lei dice che si è tentato di aggiungere:

Rect& operator=(const Rect&) = default;

Tuttavia, questo farebbe alcuna differenza. La funzione operator= generata dal compilatore è delete d perché non è possibile per il compilatore generarne una (la spiegazione per questo viene fornita di seguito); scrivere = default; non cambia questo. È necessario scrivere il proprio corpo per operator= che esegue le azioni che si desidera eseguire quando si verifica un compito.


In Standard C++ non è consentito avere una struttura anonima, per non parlare di una struttura anonima all'interno di un'unione anonima. Quindi sei davvero fuori da solo qui. Le regole che il compilatore sta utilizzando in merito a operator=, costruttore di copia, ecc. Non sono coperte da alcuno Standard.

una versione del Rect che è compilabile in standard C potrebbe essere simile:

class Rect 
{ 
public: 
    struct S1 { 
     Vector p1, p2; 
     S1(Vector p1, Vector p2): p1(p1), p2(p2) {} 
    }; 
    struct S2 { 
     float p1x, p1y, p2x, p2y; 
    }; 

    union { 
     struct S1 s1; 
     struct S2 s2; 
    }; 

    Rect() : s1({0, 0}, {0, 0}) {} 
    Rect(Vector p1, Vector p2) : s1(p1, p2) {} 
}; 

Fin qui, tutto bene. Per questa classe, lo operator= implicitamente dichiarato è definito come cancellato. Per capire perché, dobbiamo prima esaminare le funzioni speciali dichiarate implicitamente per l'unione anonima, perché il comportamento della funzione implicitamente dichiarata per una classe dipende dal comportamento della stessa operazione per ciascuno dei suoi membri.

La regola qui rilevante per l'unione è C++ 14 [class.union]/1:

Se un membro di dati non-statico di un sindacato ha un costruttore di default non banale, costruttore di copia , spostare costruttore, copiare l'operatore di assegnazione, spostare l'operatore di assegnazione o distruttore, la funzione membro corrispondente dell'unione deve essere fornita dall'utente o sarà implicitamente delete d per l'unione.

Vector ha un non banale operator=, perché si scrive il proprio corpo per esso. Pertanto, S1 ha un valore non banale operator=, perché ha un membro con non banale operator=, quindi, in base alla citazione precedente, il operator= implicitamente dichiarato per l'unione è delete d.

Si noti che non vi è alcun errore sul copy-constructor: Vectorfa ha un banale copy-constructor, quindi anche il sindacato lo fa.


Per correggere questo errore che si possa fare una delle due cose:

  • Change Vector::operator= di essere banale, sia rimuovendo la tua definizione del tutto, o rendere = default;
  • Write operator= per la Rect classe

Ora, come scriveresti il ​​tuo operator=? Lo fai s1 = other.s1; o lo fai s2 = other.s2;? Il compilatore non può saperlo da solo, che è la ragione per cui lo operator= implicitamente dichiarato viene eliminato.

Ora, sembra che trascurato (accidentalmente o deliberatamente) la regola di membri attivi in ​​C++:

In un'unione, tutt'al più uno dei membri di dati non statici può essere attivo in qualsiasi momento

Questo significa che se s1 è l'ultimo set di membri, allora dovreste fare s1 = other.s1;. Oppure se s2 è l'ultimo membro impostato, dovresti fare s2 = other.s2;.

Il costruttore di copie non si imbatte in questo problema perché è banale: il compilatore può generare una copia bit-saggio e che implementerà correttamente la copia indipendentemente dal membro attivo. Ma dal momento che il tuo operator= non è banale, non sarebbe possibile.

Ad esempio, immagina se hai effettivamente un'unione di std::string e std::vector - la copia bit a bit non funziona per nessuno di questi e devi sapere quale è attivo per poter eseguire la copia.


Ribadendo: In serie C++ non è permesso di leggere un membro di un sindacato diverso da quello più recente scritto. Non puoi usare i sindacati per l'aliasing. C++ ha altri strumenti linguistici per ottenere ciò che potresti fare in C con l'aliasing unione, see here for more discussion.

In base alla scelta dei membri per le strutture anonime, sospetto che questo è ciò che si intendeva fare. Se vuoi davvero andare avanti con questo approccio, facendo affidamento sul tuo compilatore per implementare l'aliasing unione come un'estensione non standard, allora il mio consiglio sarebbe di usare il valore predefinito operator= per la tua classe Vector.

Problemi correlati