Ich erstelle eine Klasse von Verkettungstypen, wie das kleine Beispiel unten. Es scheint, dass beim Verketten von Elementfunktionen der Kopierkonstruktor aufgerufen wird. Gibt es eine Möglichkeit, den Aufruf des Kopierkonstruktors loszuwerden? In meinem Spielzeugbeispiel unten ist es offensichtlich, dass ich mich nur mit Provisorien beschäftige und daher "sollte" (vielleicht nicht nach den Maßstäben, aber logisch) eine Elision sein. Die zweitbeste Wahl, um elision zu kopieren, wäre, den Verschiebungskonstruktor aufzurufen, aber dies ist nicht der Fall.
class test_class {
private:
int i = 5;
public:
test_class(int i) : i(i) {}
test_class(const test_class& t) {
i = t.i;
std::cout << "Copy constructor"<< std::endl;
}
test_class(test_class&& t) {
i = t.i;
std::cout << "Move constructor"<< std::endl;
}
auto& increment(){
i++;
return *this;
}
};
int main()
{
//test_class a{7};
//does not call copy constructor
auto b = test_class{7};
//calls copy constructor
auto b2 = test_class{7}.increment();
return 0;
}
Bearbeiten: Einige Klarstellungen. 1. Dies hängt nicht von der Optimierungsstufe ab. 2. In meinem realen Code habe ich komplexere (z. B. Heap zugewiesene) Objekte als Ints
auto b = test_class{7};
ruft den Kopierkonstruktor nicht auf, weil er wirklich gleichwertig isttest_class b{7};
und Compiler klug genug sind, um diesen Fall zu erkennen, und daher das Kopieren leicht vermeiden können. Das gleiche kann man nicht machenb2
.std::cout
) in Ihrem Kopierer? Ohne sie sollte die Kopie weg optimiert werden.Antworten:
Teilantwort (es wird nicht
b2
an Ort und Stelle konstruiert , sondern verwandelt die Kopierkonstruktion in eine Verschiebungskonstruktion): Sie können dieincrement
Elementfunktion für die Wertekategorie der zugeordneten Instanz überladen :Dies bewirkt
zu verschieben-konstruieren,
b2
weiltest_class{7}
es ein temporäres ist und die&&
Überladung vontest_class::increment
aufgerufen wird.Für eine echte In-Place-Konstruktion (dh nicht einmal eine Verschiebungskonstruktion) können Sie alle speziellen und nicht speziellen Elementfunktionen in
constexpr
Versionen umwandeln. Dann können Sie tunund Sie müssen weder einen Umzug noch eine Kopierkonstruktion bezahlen. Dies ist natürlich für das einfache
test_class
, aber nicht für ein allgemeineres Szenario möglich, das keineconstexpr
Mitgliedsfunktionen zulässt .quelle
b2
nicht modifizierbar.constinit
wäre der Weg dorthin.this
genau wieconst
Mitgliedsfunktionen istGrundsätzlich erfordert das Zuweisen eines Verweises auf einen Wert das Aufrufen eines Konstruktors, dh einer Kopie oder einer Verschiebung . Dies unterscheidet sich von der Kopierelision, bei der auf beiden Seiten der Funktion bekannt ist, dass es sich um dasselbe unterschiedliche Objekt handelt. Eine Referenz kann sich ähnlich wie ein Zeiger auf ein freigegebenes Objekt beziehen.
Der einfachste Weg ist wahrscheinlich, den Kopierkonstruktor vollständig zu optimieren. Die Werteinstellung wird bereits vom Compiler optimiert, es ist nur die
std::cout
, die nicht weg optimiert werden kann.(oder entfernen Sie einfach sowohl den Kopier- als auch den Verschiebungskonstruktor)
Live-Beispiel
Da Ihr Problem im Wesentlichen mit der Referenz zusammenhängt, gibt eine Lösung wahrscheinlich keine Referenz auf das Objekt zurück, wenn Sie das Kopieren auf diese Weise beenden möchten.
Eine dritte Methode basiert in erster Linie auf der Kopierentfernung. Dies erfordert jedoch ein Umschreiben oder Einkapseln des Vorgangs in eine Funktion, um das Problem insgesamt zu vermeiden (ich bin mir bewusst, dass dies möglicherweise nicht das ist, was Sie möchten, aber es könnte ein Problem sein Lösung für andere Benutzer):
Eine vierte Methode verwendet stattdessen eine Verschiebung, die entweder explizit aufgerufen wird
oder wie in dieser Antwort zu sehen .
quelle