Frage

Hier ist ein Mindestcodebeispiel, das das Problem veranschaulicht:

#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);
}

Dies ergibt den gleichen Fehler:

int main()
{
    show( Thing(3) );
}

IBM XL C/C ++ 8.0 Compiler unter AIX emittiert diese Warnungen:

"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.

Ich habe auch G ++ 4.1.2 mit "-wall" und "-pedantisch" ausprobiert und habe keine Diagnose. Warum ist der Zugriff auf den Kopierkonstruktor hier erforderlich? Wie kann ich die Warnung beseitigen, abgesehen davon, dass das Objekt kopierbar ist (was außerhalb meiner Kontrolle liegt) oder eine explizite Kopie zum Übergeben erstellen (wenn das reale Objekt teuer zu kopieren ist)?

War es hilfreich?

Lösung

Die Regeln hierfür sind in §8.5.3/5 des Standards. Es gibt drei grundlegende Situationen. Der erste betrifft den Initialisierer ('3' in Ihrem Fall) entweder ein LVALUE oder einen Klassentyp. Da keiner davon wahr ist, ist das, was Sie haben, der dritte Fall: Initialisierung einer CONT -Referenz mit einem RValue, der keinen Klassentyp hat. Dieser Fall wird von der endgültigen Kugel in 8.5.3/5 abgedeckt:

Andernfalls wird ein temporärer Typ „CV1 T1“ erstellt und aus dem Initializer-Ausdruck unter Verwendung der Regeln für eine Initialisierung der Nicht-Referenzkopie (8.5) erstellt und initialisiert. Die Referenz wird dann an die temporäre gebunden. Wenn T1 auf T2 verwandt ist, muss CV1 die gleiche CV-Qualifikation wie oder größer als CV2 sein; Ansonsten ist das Programm schlecht geformt.

EDIT: Ich denke, IBM hat es richtig. Ich habe zuvor an die Möglichkeit gedacht, das Temporäre kopieren zu müssen, aber das ist nicht die Quelle des Problems. Um die temporäre mithilfe der in §8.5 angegebenen Nicht-Referenzkopie-Initialisierung zu erstellen, benötigt sie den Copy CTOR. Insbesondere ist es zu diesem Zeitpunkt einem Ausdruck wie:

T x = a;

Dies entspricht im Grunde:

T x = t (a);

Dh es ist erforderlich, eine vorübergehende Erstellung zu erstellen und dann das vorübergehende zum initialisierten Objekt zu kopieren (was in diesem Fall ist Auch eine temporäre). Um den erforderlichen Prozess zusammenzufassen, entspricht es ungefähr dem Code wie:

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2

Andere Tipps

C ++ ermöglicht es ausreichend überschwingliche Compiler, um vorübergehende Objekte zu vermeiden, die einzige Verstoßes gegen die als ob Regel durch den Standard zulässig. Ich bin mit dem AIX C ++ - Compiler von IBM nicht vertraut, aber es hört sich so an, als würde es denkt, das show(3) Anruf erfordert, dass eine vorübergehende Sache kopiert wird. In diesem Fall erfordert C ++, dass Sie über einen zugänglichen Kopierkonstruktor verfügen, obwohl Ihr Compiler intelligent genug ist, um ihn zu vermeiden.

Aber warum tut es show(3) Erfordern Sie überhaupt eine Kopie? Das kann ich nicht herausfinden. Mit Glück wird LitB ein wenig mitmachen.

Mein Bauchgefühl ist, dass Jerry's Antworten ist korrekt, aber es gibt noch einige Fragen.

Interessant ist, dass es ein Kernproblem gibt, das den vorherigen Absatz dieses Abschnitts abdeckt (391). Dieses Problem bezieht sich, wenn das Argument der gleiche Klassentyp ist. Speziell:

int main () {
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391
}

Die Änderung des Kernproblems 391 nur betrifft, wo das temporäre RValue den gleichen Klassentyp hat. Der vorherige Wortlaut hatte:

Wenn der Initializer -Ausdruck ein RValue ist, mit T2 ein Klassentyp und cv1 T1 ist Referenzkompatibel mit cv2 T2, Die Referenz ist wie folgt gebunden:

[...]

Der Konstruktor, mit dem die Kopie hergestellt wird, ist aufrufbar, ob die Kopie tatsächlich erledigt ist oder nicht.

Diese letzte Zeile ist das, was machen würde show(Thing(3)) illegal gemäß dem aktuellen Standard. Der vorgeschlagene Wortlaut für diesen Abschnitt lautet:

Wenn der Initializer-Expression ein RValue mit T2 ein Klassentyp ist und "CV1 T1" Referenzkompatibel mit "CV2 T2" ist, ist die Referenz an das vom RValue dargestellte Objekt (siehe 3.10 [Basic.lval]) oder an das Objekt gebunden, oder zu einem Unterobjekt innerhalb dieses Objekts.

Zu diesem Zeitpunkt habe ich nachgedacht, dass G ++ sein Verhalten wie per aktualisiert hat 391 Die Änderung enthielt jedoch versehentlich den Fall der Kopieinitialisierung. Dies wird jedoch nicht durch die Versionen von G ++ demonstriert, mit denen ich getestet habe:

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

Ich kann in Jerrys Interpretation für die keine Schuld finden foo (3) Der Fall habe jedoch Zweifel aufgrund der Diskrepenz zwischen den verschiedenen Compiler -Verhaltensweisen.

Was passiert, wenn Sie versuchen, die vorübergehende Sache zu benennen?

Thing temp(3);
show(temp);

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top