Operatore di assegnazione copia predefinito con membro di riferimento? [duplicare]
Sto lavorando a un'applicazione legacy ibrida (C ++ / C #) in Visual Studio 2017, per la quale sto attualmente cercando di aggiungere la registrazione a una classe esistente per aiutare a diagnosticare un altro problema. La classe in questione è abbastanza semplice:
class CAnExampleClass : CAnExampleBaseClass
{
public:
CAnExampleClass();
virtual ~CAnExampleClass(void);
// dozens of public methods
private:
// a few private methods
public:
// dozens of public member variables, all intrinsic types or custom types with copy constructors or arrays thereof
private:
class AnExampleChildImpl;
mutable std::shared_ptr<AnExampleChildImpl> _pImpl;
// a couple of friend classes
bool _anExampleFlag;
public:
static const int NumberOfItems = 15;
}
Nell'implementazione il costruttore inizializza il membro privato nell'elenco di inizializzazione in questo modo:
CAnExampleClass::CAnExampleClass()
: _anExampleFlag(false)
Seguendo lo schema stabilito altrove nell'applicazione, ho aggiunto un membro alla classe per il logger:
private:
ILog& _log;
E ha inizializzato questo membro nell'elenco di inizializzazione del costruttore:
CAnExampleClass::CAnExampleClass()
: _anExampleFlag(false), _log(CMyLog::GetLogger("log_name"))
Il logger è abbastanza semplice:
class ILog
{
...
}
...
class CMyLog
{
public:
...
static ILog& GetLogger(const char *loggerName);
...
}
Tuttavia, ora il compilatore segnala un errore:
Error C2280 'CAnExampleClass &CAnExampleClass::operator =(const CAnExampleClass &)': attempting to reference a deleted function ...
A quanto pare, ci sono alcuni punti in tutta l'applicazione in cui il codice si basa sull'operatore di assegnazione di copia predefinito per copiare un'istanza esistente di questa classe in una nuova istanza. Accade anche che negli altri casi in cui il logger sia già stato utilizzato non ci siano tentativi di copiare la classe utilizzando l'operatore di assegnazione copia.
La mia domanda è, quindi, esiste un'alternativa alla definizione di un operatore di assegnazione di copia personalizzato CAnExampleClass
e alla copia di ciascun membro (pubblico) e matrice nella nuova istanza? Mi sembra eccessivo che ciò sia necessario quando il membro di riferimento introdotto viene inizializzato nell'elenco di inizializzazione del costruttore: in questo caso, cosa impedisce all'operatore di assegnazione della copia predefinito di funzionare come prima? Perché devo definire un'implementazione personalizzata dell'operatore quando farà, essenzialmente, esattamente la stessa cosa dell'implementazione predefinita poiché l'elenco di inizializzazione si prende cura del membro di riferimento? Esiste un modo per fare in modo che l'operatore di assegnazione della copia predefinito ignori il membro di riferimento o in qualche modo richiami la logica di copia predefinita da un'implementazione dell'operatore personalizzato?
La modifica del codice al di fuori di questa classe per affrontare questo problema non è un'opzione realistica (l'applicazione è troppo grande, vecchia, ingombrante, ecc.) Idealmente, vorrei evitare l'opzione di implementazione dell'operatore di assegnazione personalizzata se esiste un modo più semplice, poiché (ovviamente) evita di dover ricreare manualmente tutte quelle assegnazioni dei membri, ma anche per evitare la possibilità che se un altro sviluppatore del team aggiunge successivamente un membro a questa classe possa inavvertitamente dimenticare di aggiornare l'operatore di assegnazione della copia e finiamo per con qualche strano errore logico nel codice che si basa su quell'operatore.
Risposte
cosa impedisce all'operatore di assegnazione delle copie predefinito di funzionare come prima?
Hai presentato un membro a CAnExampleClass
di tipo ILog&
. I riferimenti non sono richiudibili. Una volta inizializzato, non potrà mai cambiare. Anche se, a causa del tuo pattern singleton, le istanze di ILog
saranno sempre le stesse, avere un membro di riferimento cancellerà automaticamente le funzioni membro di assegnazione predefinita.
O si implementa l'operatore di assegnazione per tutte le classi che hanno un membro di riferimento, che non sembra essere fattibile per te, oppure si elimina il riferimento grezzo a favore di std::reference_wrapper:
std::reference_wrapper<ILog> _log; // <- reseatable reference
oppure si elimina completamente il membro e si chiama sempre CMyLog::GetLogger("log_name")
quando è necessario l'oggetto log, magari incapsulandolo in una funzione membro privata statica:
static ILog& logger() {
// this ONLY works if GetLogger always returns the same object
return CMyLog::GetLogger("log_name");
}