Was ist die Auswahlregel für den Kopier-/Verschiebungskonstruktor in C++? Wann kommt es zum Fallback zum Verschieben auf Kopie?

Lesezeit: 4 Minuten

Was ist die Auswahlregel fur den Kopier Verschiebungskonstruktor in C Wann
Saft

Das erste Beispiel:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&) = delete;
    A(A&&) = default;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

Es funktioniert perfekt. Hier wird also der MOVE-Konstruktor verwendet.

Entfernen wir den Verschiebekonstruktor und fügen Sie einen Kopierkonstruktor hinzu:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&a) 
        : ref( a.ref.get() ? new int(*a.ref) : nullptr )
    {  }
    A(A&&) = delete;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

Jetzt fällt die Kompilierung mit dem Fehler “Verwendung der gelöschten Funktion ‘A::A(A&&)’
Der MOVE-Konstruktor ist also ERFORDERLICH und es gibt keinen Rückfall auf einen COPY-Konstruktor.

Jetzt entfernen wir sowohl den Kopier- als auch den Verschiebe-Konstruktor:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

Und es fällt mit “Verwendung der gelöschten Funktion ‘A::A(const A&)’” Kompilierungsfehler. Jetzt ERFORDERT es einen COPY-Konstruktor!
Es gab also einen Fallback (?) vom Verschiebekonstruktor zum Kopierkonstruktor.

Wieso den? Hat jemand eine Ahnung, wie es dem C++-Standard entspricht und was eigentlich die Regel ist, zwischen den Kopier-/Verschiebungskonstruktoren auszuwählen?

  • Habe ich genau diese Frage nicht schon vor einer halben Stunde beantwortet >.>

    – MM

    11. Juli ’14 um 9:27

  • Diese Frage ist viel spezifischer. Es ist nicht dieselbe Frage.

    – Saft

    11. Juli ’14 um 9:44

1641749257 646 Was ist die Auswahlregel fur den Kopier Verschiebungskonstruktor in C Wann
MM

Es gibt keinen “Rückfall”. Es wird genannt Überlastauflösung. Wenn es mehr als einen möglichen Kandidaten für die Überladungsauflösung gibt, wird die beste Übereinstimmung nach einem komplizierten Regelwerk ausgewählt, das Sie durch das Lesen des C++-Standards oder eines Entwurfs davon finden können.

Hier ist ein Beispiel ohne Konstruktoren.

class X { };

void func(X &&) { cout << "moven"; }            // 1
void func(X const &)  { cout << "copyn"; }      // 2

int main()
{
    func( X{} );
}
  • Ist: druckt “move”
  • Auskommentieren “1”: druckt “Kopie”
  • “2” auskommentieren: gibt “move” aus
  • „1“ und „2“ auskommentieren: Kompilieren fehlgeschlagen

Bei der Überladungsauflösung hat die Bindung von rvalue an rvalue eine höhere Präferenz als lvalue an rvalue.


Hier ist ein sehr ähnliches Beispiel:

void func(int) { cout << "intn"; }      // 1
void func(long) { cout << "longn"; }    // 2

int main()
{
     func(1);
}
  • Ist: druckt “int”
  • Auskommentieren “1”: druckt “lang”
  • “2” auskommentieren: gibt “int” aus
  • „1“ und „2“ auskommentieren: Kompilieren fehlgeschlagen

Bei der Überlastungsauflösung wird eine genaue Übereinstimmung einer Konvertierung vorgezogen.


In Ihren drei Beispielen in diesem Thread haben wir:

1: Zwei Kandidatenfunktionen; rvalue bevorzugt rvalue (wie in meinem ersten Beispiel)

A(const A&);
A(A&&);           // chosen

2: Zwei Kandidatenfunktionen; rvalue bevorzugt rvalue (wie in meinem ersten Beispiel)

A(const A&); 
A(A&&);           // chosen

3: Eine Kandidatenfunktion; kein Wettbewerb

A(const A&);      // implicitly declared, chosen

Wie bereits erwähnt, gibt es im Fall 3 keine implizite Deklaration von A(A&&), da Sie einen Destruktor haben.

Für die Überladungsauflösung spielt es keine Rolle, ob der Funktionsrumpf existiert oder nicht, sondern ob die Funktion deklariert ist (entweder explizit oder implizit).

Was ist die Auswahlregel fur den Kopier Verschiebungskonstruktor in C Wann
Joseph Mansfield

Die zu verwendende Funktion wird ausgewählt, bevor geprüft wird, ob dies der Fall ist deleted oder nicht. Für den Fall, dass der Kopierkonstruktor verfügbar war und der Verschiebekonstruktor war deleted, der Move-Konstruktor war immer noch die beste Wahl von beiden. Dann sieht es, dass es ist deleted und gibt Ihnen einen Fehler.

Wenn Sie das gleiche Beispiel hätten, aber den Move-Konstruktor tatsächlich entfernt haben, anstatt ihn zu erstellen deleted, Sie werden sehen, dass es sich gut kompilieren lässt und auf den Kopierkonstruktor zurückgreift:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&a) 
        : ref( a.ref.get() ? new int(*a.ref) : nullptr )
    {  }
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

Für diese Klasse ist überhaupt kein Bewegungskonstruktor deklariert (auch nicht implizit), daher kann sie nicht ausgewählt werden.

  • Die Wahl (der Auswahlprozess) hängt also davon ab, ob es einen Konstruktor oder zwei gibt? Das scheint verkabelt zu sein. Ein gelöschter Konstruktor und ein fehlender Konstruktor sind also nicht dasselbe?

    – Saft

    11. Juli ’14 um 9:16

  • @ user3544995 Der Auswahlprozess hängt davon ab, welche Konstruktoren deklariert sind. Es wählt den Konstruktor mit der besten Übereinstimmung, wie in allen Überladungssituationen. Und ja, a deleted-Konstruktor und fehlender Konstruktor sind nicht unbedingt das gleiche. Es ist möglich, dass ein fehlender Konstruktor implizit als . deklariert wird deleted allerdings. In diesem Fall ist dies nicht der Fall – es wird nur implizit überhaupt nicht deklariert.

    – Joseph Mansfield

    11. Juli ’14 um 9:17


.

228940cookie-checkWas ist die Auswahlregel für den Kopier-/Verschiebungskonstruktor in C++? Wann kommt es zum Fallback zum Verschieben auf Kopie?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy