Perché questo avviso dal compilatore C / C ++ IBM XL?
-
06-07-2019 - |
Domanda
Ecco un esempio di codice minimo che illustra il problema:
#include <iostream>
class Thing
{
// Non-copyable
Thing(const Thing&);
Thing& operator=(const Thing&);
int n_;
public:
Thing(int n) : n_(n) {}
int getValue() const { return n_;}
};
void show(const Thing& t)
{
std::cout << t.getValue() << std::endl;
}
int main()
{
show(3);
}
Questo produce lo stesso errore:
int main()
{
show( Thing(3) );
}
Il compilatore IBM XL C / C ++ 8.0 in AIX emette questi avvisi:
"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.
Ho anche provato g ++ 4.1.2 con " -Wall " e "convenzionale" e non ho avuto diagnosi. Perché è richiesto l'accesso al costruttore di copie qui? Come posso eliminare l'avvertimento, oltre a rendere l'oggetto copiabile (che è al di fuori del mio controllo) o fare passare una copia esplicita (quando l'oggetto nella vita reale è costoso da copiare)?
Soluzione
Le regole per questo sono in & # 167; 8.5.3 / 5 dello standard. Sono state identificate tre situazioni di base. Il primo prevede che l'inizializzatore ('3' nel tuo caso) sia o un valore o con un tipo di classe. Poiché nessuno di questi è vero, quello che hai è il terzo caso: inizializzare un riferimento const con un valore che non ha un tipo di classe. Questo caso è coperto dal proiettile finale in 8.5.3 / 5:
Altrimenti, un temporaneo di tipo & # 8220; cv1 T1 & # 8221; viene creato e inizializzato dall'espressione dell'inizializzatore usando le regole per un'inizializzazione della copia senza riferimento (8.5). Il riferimento viene quindi associato al temporaneo. Se T1 è correlato al riferimento a T2, cv1 deve avere la stessa qualifica cv o una qualifica cv maggiore rispetto a cv2; in caso contrario, il programma è mal formato.
Modifica: rileggere, penso che IBM abbia ragione. In precedenza stavo pensando alla possibilità di dover copiare il temporaneo, ma non è questa la fonte del problema. Per creare l'inizializzazione temporanea utilizzando la copia senza riferimento come specificato in & # 167; 8.5, è necessario il ctor copia. In particolare, a questo punto equivale a un'espressione come:
T x = a;
Ciò equivale sostanzialmente a:
T x = T (a);
vale a dire. è necessario creare un temporaneo, quindi copiarlo sull'oggetto che si sta inizializzando (che, in questo caso, è anche un temporaneo). Per riassumere il processo richiesto, è approssimativamente equivalente a un codice come:
T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2); // show's reference parameter binds directly to temp2
Altri suggerimenti
C ++ consente a compilatori sufficientemente intelligenti di evitare di copiare oggetti temporanei, l'unica violazione della regola come-se consentita dallo standard. Non ho familiarità con il compilatore AIX C ++ di IBM, ma sembra che pensi che la chiamata show (3)
richieda una cosa temporanea da copiare. In tal caso, C ++ richiede che tu abbia un costruttore di copie accessibile anche se il tuo compilatore è abbastanza intelligente da evitare di usarlo.
Ma perché show (3)
richiede innanzitutto una copia? Che non riesco a capire. Con un po 'di fortuna, il litb arriverà tra poco.
La mia sensazione è che la di Jerry rispondi è corretto, ma ci sono ancora alcune domande.
Ciò che è interessante è che c'è un problema fondamentale che copre il paragrafo precedente di quella sezione ( 391 ). Tale problema riguarda quando l'argomento è dello stesso tipo di classe. In particolare:
int main () {
show ( Thing (3) ); // not allowed under current wording
// but allowed with Core Issue 391
show ( 3 ); // Still illegal with 391
}
La modifica del problema principale 391 riguarda solo dove il rvalue temporaneo ha lo stesso tipo di classe. La formulazione precedente aveva:
Se l'espressione dell'inizializzatore è un valore rvalore, con T2 un tipo di classe e
cv1 T1
è compatibile con il riferimento concv2 T2,
il riferimento è associato come segue:[...]
Il costruttore che verrebbe utilizzato per creare la copia deve essere richiamabile indipendentemente dal fatto che la copia sia stata effettivamente eseguita.
L'ultima riga è ciò che renderebbe show (Thing (3))
illegale secondo lo standard attuale. La formulazione proposta per questa sezione è:
Se l'espressione dell'inizializzatore è un valore rvalore, con T2 un tipo di classe e "cv1 T1" è compatibile con riferimento a "cv2 T2", il riferimento è associato all'oggetto rappresentato dal valore (vedi 3.10 [basic.lval]) o ad un oggetto secondario all'interno di tale oggetto.
A questo punto, ho considerato che g ++ potrebbe aver aggiornato il suo comportamento secondo 391 ma che la modifica ha incluso accidentalmente il caso di inizializzazione della copia. Tuttavia, ciò non è dimostrato dalle versioni di g ++ che ho testato con:
class A{
public:
A ();
A (int);
private:
A (A const &);
};
void foo (A const &);
void foo ()
{
A a = 3 ; // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)
foo ( 3 ) ; // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
foo ( A() ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
}
Non riesco a trovare difetti nell'interpretazione di Jerry per il caso foo (3)
, tuttavia ho dubbi a causa della discrepanza tra i diversi comportamenti del compilatore.
Cosa succede se provi a nominare la cosa temporanea?
Thing temp (3);
show (temp);