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)?

È stato utile?

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 con cv2 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);

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top