<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Member im Konstruktor per Move oder Copy initialisieren?]]></title><description><![CDATA[<p>Hallo zusammen,</p>
<p>Ich programmiere schon seit ein paar Jahren mit C++ und ich mache mir Gedanken, wie ich am besten die Member einer Klasse initialisieren kann. Bisher habe ich im Konstruktor meistens const Referenzen verwendet. Ich möchte möglichst effizient arbeiten und einen gewissen Leitfaden folgen. Ausnahmen bestätigen die Regeln.</p>
<pre><code class="language-cpp">class TestClass
{
    std::string Wert1;
    int Wert2;
public:
    TestClass(
        const std::string&amp; wert1,
        const int wert2
    ) : Wert1(wert1),
        Wert2(wert2)
    {};
};
</code></pre>
<p>Das sorgt natürlich für Kopien.  Kann manchmal gewünscht sein oder auch nicht. Hat natürlich den Vorteil, das die Variablen des Aufrufers nicht verändert werden und er diese weiter verwenden kann. Aber wenn der Aufrufer die Objekte direkt in der Parameterübergabe vom Konstruktor übergibt, werden unnötige Kopien gemacht.</p>
<pre><code class="language-cpp">TestClass temp(
        std::string(&quot;ABC&quot;),
        5
    );
</code></pre>
<p>Die temporäre Variable/Objekt vom Typ String wird kopiert und dann zerstört. In letzter Zeit habe ich mir den Move Operator angeschaut.</p>
<pre><code class="language-cpp">TestClass(
        std::string wert1,
        int wert2
    ) : Wert1(std::move(wert1)),
        Wert2(std::move(wert2))
    {};
</code></pre>
<p>In diesem Fall werden die Werte gemovt ohne kopiert zu werden. Wenn der Aufrufer bei der Parameterübergabe diese Werte in den Konstruktor movt, werden auch keine lokalen Kopien in der Parameterliste erzeugt und Kopien werden vermieden. Allerdings muss der Aufrufer dann auch explizit die Variablen in den Konstruktor beim Erzeugen des Objekts moven.</p>
<p>Beide Varianten haben Vor- und Nachteile und ich würde mal eure Erfahrungen dazu hören, was ihr macht oder machen würden. Ob ihr einen Leitfaden habt, dass ihr bei Klassen meist movt oder wie ich immer mit const Referenzen und Kopien arbeitet?<br />
Ich könnte natürlich auch immer 2 Konstruktoren machen. Einen der ByValue die Variablen annimmt und daher das moven unterstützt und einen der die Variablen ByConstReference annimmt.</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/354360/member-im-konstruktor-per-move-oder-copy-initialisieren</link><generator>RSS for Node</generator><lastBuildDate>Thu, 08 Oct 2026 12:33:57 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/354360.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 26 Oct 2023 18:24:37 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Thu, 26 Oct 2023 18:24:37 GMT]]></title><description><![CDATA[<p>Hallo zusammen,</p>
<p>Ich programmiere schon seit ein paar Jahren mit C++ und ich mache mir Gedanken, wie ich am besten die Member einer Klasse initialisieren kann. Bisher habe ich im Konstruktor meistens const Referenzen verwendet. Ich möchte möglichst effizient arbeiten und einen gewissen Leitfaden folgen. Ausnahmen bestätigen die Regeln.</p>
<pre><code class="language-cpp">class TestClass
{
    std::string Wert1;
    int Wert2;
public:
    TestClass(
        const std::string&amp; wert1,
        const int wert2
    ) : Wert1(wert1),
        Wert2(wert2)
    {};
};
</code></pre>
<p>Das sorgt natürlich für Kopien.  Kann manchmal gewünscht sein oder auch nicht. Hat natürlich den Vorteil, das die Variablen des Aufrufers nicht verändert werden und er diese weiter verwenden kann. Aber wenn der Aufrufer die Objekte direkt in der Parameterübergabe vom Konstruktor übergibt, werden unnötige Kopien gemacht.</p>
<pre><code class="language-cpp">TestClass temp(
        std::string(&quot;ABC&quot;),
        5
    );
</code></pre>
<p>Die temporäre Variable/Objekt vom Typ String wird kopiert und dann zerstört. In letzter Zeit habe ich mir den Move Operator angeschaut.</p>
<pre><code class="language-cpp">TestClass(
        std::string wert1,
        int wert2
    ) : Wert1(std::move(wert1)),
        Wert2(std::move(wert2))
    {};
</code></pre>
<p>In diesem Fall werden die Werte gemovt ohne kopiert zu werden. Wenn der Aufrufer bei der Parameterübergabe diese Werte in den Konstruktor movt, werden auch keine lokalen Kopien in der Parameterliste erzeugt und Kopien werden vermieden. Allerdings muss der Aufrufer dann auch explizit die Variablen in den Konstruktor beim Erzeugen des Objekts moven.</p>
<p>Beide Varianten haben Vor- und Nachteile und ich würde mal eure Erfahrungen dazu hören, was ihr macht oder machen würden. Ob ihr einen Leitfaden habt, dass ihr bei Klassen meist movt oder wie ich immer mit const Referenzen und Kopien arbeitet?<br />
Ich könnte natürlich auch immer 2 Konstruktoren machen. Einen der ByValue die Variablen annimmt und daher das moven unterstützt und einen der die Variablen ByConstReference annimmt.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618660</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618660</guid><dc:creator><![CDATA[KK27]]></dc:creator><pubDate>Thu, 26 Oct 2023 18:24:37 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Thu, 26 Oct 2023 19:18:46 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/38371">@KK27</a> sagte in <a href="/forum/post/2618660">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>In diesem Fall werden die Werte gemovt ohne kopiert zu werden. Wenn der Aufrufer bei der Parameterübergabe diese Werte in den Konstruktor movt, werden auch keine lokalen Kopien in der Parameterliste erzeugt und Kopien werden vermieden. Allerdings muss der Aufrufer dann auch explizit die Variablen in den Konstruktor beim Erzeugen des Objekts moven.</p>
</blockquote>
<p>Nein, der Aufrufer muss nur dann explizit moven, wenn er auch möchte, dass die Klasse das Objekt, bzw. die dahinter stehenden Ressourcen &quot;in Besitz&quot; nimmt. Z.B.</p>
<pre><code class="language-cpp">std::string s = &quot;ABC&quot;;
TestClass temp(std::move(s),  5);
// Ich will hier nichts mehr mit s machen und möchte, dass TestClass 
// den String übernimmt.
</code></pre>
<p>Das dürfte jedoch eher selten der Fall sein. Meistens will man ohnehin eine separate Kopie in dem neuen Objekt haben, d.h. man braucht auch kein <code>std::move</code>, da man sowieso einmal kopieren muss:</p>
<pre><code class="language-cpp">std::string s = &quot;ABC&quot;;
TestClass temp(s,  5);
// Ich brauche s hier noch.
</code></pre>
<p>Eine temporäres Objekt wie in diesem Fall:</p>
<pre><code class="language-cpp">TestClass temp(std::string(&quot;ABC&quot;), 5);
</code></pre>
<p>oder auch</p>
<pre><code class="language-cpp">TestClass temp(&quot;ABC&quot;, 5);
</code></pre>
<p>Ist sowieso ein <em>xvalue</em> und damit ein <em>rvalue</em>, der an den bereits vorhandenen Move-Konstruktor des <code>std::string</code> binden kann. Da braucht man ebenfalls kein <code>std::move</code>, denn <code>std::string(std::string&amp;&amp;)</code> movt hier bereits automatisch.</p>
<p>Insofern ist die zweite Variante, den String <em>By Value</em> entgegenzunehmen und während der Initialisierung in den Daten-Member zu moven m.E. eine gute Lösung, um überflüssige Kopien zu vermeiden. Für den <code>int</code> ist das hier nicht notwendig, da den zu kopieren praktisch nichts kostet, aber schlechterer Code wird damit auch nicht erzeugt (lediglich überflüssige Tipparbeit).</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618664</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618664</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Thu, 26 Oct 2023 19:18:46 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Thu, 26 Oct 2023 19:28:59 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618664">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Nein, der Aufrufer muss nur dann explizit moven, wenn er auch möchte, dass die Klasse das Objekt, bzw. die dahinter stehenden Ressourcen &quot;in Besitz&quot; nimmt. Z.B.</p>
</blockquote>
<p>Ja das ist mir klar. Hab es etwas unklar beschrieben.</p>
<p>In meinem aktuellen Projekt auf der Arbeit habe ich teilweise einige Objekte aus einer Datenbank, die mit anderen Tabellen verknüpft sind und eine 1 zu n Beziehung haben. Die Objekte aus dem Left Join sammel ich in Vektoren über eine Schleife und übergebe sie dann den &quot;Mutter&quot; Objekt. Bisher habe ich in die Vektoren im Konstruktor immer als const Referenz angeommen und damit unnötige Kopien gemacht. Die Vektoren danach habe ich nicht mehr weiter verwendet.</p>
<p>Ich hab mich dann mit dem Move Konstruktoren und Operator(en) auseinander gesetzt und gemerkt, dass ich beim Moven unnötige Kopien vermeiden kann.<br />
Ein Kollege von mir in der Arbeit nimmt in seinen Konstruktoren fast alles per Value an und movt es dann in die Member, wenn er es in den Membern braucht.</p>
<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618664">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Insofern ist die zweite Variante, den String By Value entgegenzunehmen und während der Initialisierung in den Daten-Member zu moven m.E. eine gute Lösung, um überflüssige Kopien zu vermeiden. Für den int ist das hier nicht notwendig, da den zu kopieren praktisch nichts kostet, aber schlechterer Code wird damit auch nicht erzeugt (lediglich überflüssige Tipparbeit).</p>
</blockquote>
<p>Das man ein int nicht moven kann bzw. muss ist mir klar, aber der Vollständigkeit Halber habe ich es dennoch davor gesetzt.</p>
<p>Vermeidest du persönlich const Referenzen als Parameter in Konstruktoren(vom Copy Konstruktor abgesehen)?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618665</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618665</guid><dc:creator><![CDATA[KK27]]></dc:creator><pubDate>Thu, 26 Oct 2023 19:28:59 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Thu, 26 Oct 2023 19:47:51 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/38371">@KK27</a> sagte in <a href="/forum/post/2618665">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Vermeidest du persönlich const Referenzen als Parameter in Konstruktoren(vom Copy Konstruktor abgesehen)?</p>
</blockquote>
<p>Wenn das Objekt eine eigene Kopie von dem Parameter z.B. in einer Member-Variablen bekommen soll, mache ich es wie deine zweite Variante. <em>By Value</em> und dann <code>move</code>. Ansonsten ist bei mir auch erstmal <code>const&amp;</code> Standard.</p>
<p>By Value + Move ist erstens ein sehr simples Pattern und hat zweitens den Vorteil, dass der Anwender entscheiden kann, ob er moven will oder nicht. Wenn man als <code>const&amp;</code> entgegennimmt, wird dem Anwender nämlich die Kopie aufgezwungen. Alternativ kann man auch mit <code>std::string&amp;&amp;</code>-Parametern rumhantieren, das macht es aber nur unnötig kompliziert und damit lässt sich bestenfalls ein ohnehin recht billiges Move einsparen.</p>
<p>Und natürlich geht das auch analog zu <code>std::string</code> mit einem <code>std::vector</code> und allen anderen Typen, die sich sinnvoll &quot;moven&quot; lassen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618666</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618666</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Thu, 26 Oct 2023 19:47:51 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 03:16:00 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/38371">@KK27</a><br />
Ich bin bei grossen, komplexen Objekten dazu übergegangen eher auf <code>move</code> zu verzichten -- ausser die Parameter sind <em>wirklich</em> teuer zu kopieren. Beispiel:</p>
<pre><code class="language-cpp">Foo::Foo(std::shared_ptr&lt;Logger&gt; logger, int a, int b, int c, int d) :
    m_logger(std::move(logger)),
    m_component1(a),
    m_component2(m_logger, b),
    m_component3(c, d),
    ...
    ... {
...
</code></pre>
<p>Kann man so machen. Bringt aber nicht viel, weil die Initialisierung hier vermutlich schon so teuer ist, dass die 2 atomic Instructions die man sich bei der <code>std::shared_ptr</code> Kopie spart relativ gesehen nicht viel ausmachen. Umgekehrt kann man schnell den Fehler machen <code>m_component2(logger, b)</code> statt <code>m_component2(m_logger, b)</code> zu schreiben. Bzw. wenn die Reihenfolge der Membervariablen nicht passt gibt's auch ein Problem. Daher verwende ich in solchen Fällen eher <code>const&amp;</code> - weil ich da Lesbarkeit und Wartbarkeit für wichtiger halte als 20 oder 30 eingesparte CPU Zyklen.</p>
<p>In deinem Beispiel wo nur ein <code>std::string</code> und ein <code>int</code> übergeben werden finde ich &quot;by value + move&quot; aber eine gute Lösung. Aus den von <a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> beschriebenen Gründen.</p>
<p>Wobei es immer darauf ankommt wie performance-kritisch etwas ist. Wenn das <em>die</em> zentrale Klasse in deiner Anwendung ist was Performance angeht, dann könnte es Sinn machen einen Konstruktor mit <code>std::string&amp;&amp;</code> und einen mit <code>std::string const&amp;</code> zu machen. Wobei der <code>std::string&amp;&amp;</code> Konstruktor dann vermutlich auch inline sein sollte. In dem Fall wäre es aber auch wichtig einen Performance-Vergleich zu machen um zu sehen was wirklich schneller ist. Denn da kann man sich auch schnell mal verschätzen <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618673</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618673</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Fri, 27 Oct 2023 03:16:00 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 04:38:58 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/13960">@hustbaer</a> sagte in <a href="/forum/post/2618673">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>[...] dann könnte es Sinn machen einen Konstruktor mit <code>std::string&amp;&amp;</code> und einen mit <code>std::string const&amp;</code> zu machen. [...]</p>
</blockquote>
<p>Mit dem &quot;und einen&quot; lugt da breits ein kombinatorischer Alptraum über den Horizont, wenn man es mal mit mehr als einem Parameter zu tun hat, den man gerne moven möchte ... auch ein Argument für By-Value+Move <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f601.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--beaming_face_with_smiling_eyes"
      title=":beaming_face_with_smiling_eyes:"
      alt="😁"
    /></p>
<p>Selbst wenn man nicht auf bereits existierende Move-Konstruktoren wie bei <code>std::string</code> zurückgreifen kann und die selbst implementieren muss, ist das am Ende immer noch nur eine Funktion pro Klasse. Mit den Rvalue-Referenz-Parametern spart man hingegen nur einen Move im Vergleich zu By-Value+Move (den, mit dem der Parameter initialisiert wird oder der Move in die Member-Variable, wenn kopiert werden soll).</p>
<p>Für wirklich &quot;heisse&quot; Funktionen kann man das sicher rechtfertigen, für alle anderen ist  By-Value+Move allerdings meiner Meinung nach so simpel, dass es man schon fast ein gutes Argument braucht es <em>nicht</em> zu verwenden. Aber eben nur, wenn man auch eine eigene Kopie des Parameters haben will, sonst <code>const&amp;</code>.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618675</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618675</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Fri, 27 Oct 2023 04:38:58 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 12:53:21 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618675">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Mit dem &quot;und einen&quot; lugt da breits ein kombinatorischer Alptraum über den Horizont, wenn man es mal mit mehr als einem Parameter zu tun hat, den man gerne moven möchte ... auch ein Argument für By-Value+Move</p>
</blockquote>
<p>Ich teile deine Meinung. Das Thema wäre für mich dann abgeschlossen. Vielen Dank für eure beiden Antworten und Meinungen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618689</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618689</guid><dc:creator><![CDATA[KK27]]></dc:creator><pubDate>Fri, 27 Oct 2023 12:53:21 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 19:07:02 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618675">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Für wirklich &quot;heisse&quot; Funktionen kann man das sicher rechtfertigen, für alle anderen ist  By-Value+Move allerdings meiner Meinung nach so simpel, dass es man schon fast ein gutes Argument braucht es nicht zu verwenden.</p>
</blockquote>
<p>Sehe ich ähnlich.</p>
<p>Wobei mir gerade ein Detail einfällt - etwas was viele vermutlich gar nicht wissen: &quot;by value&quot; (mit oder ohne move) erfordert u.U. (je nach Calling Convention) die Generierung eines dtor Aufrufs pro Funktionsaufruf. Was auch wieder etwas Overhead und vor allem Code-Bloat erzeugt. Das ist in den meisten Fällen vermutlich auch egal - aber es gibt Spezialfälle wo es hilfreich sein kann wenn man das weiss. (Wir haben z.B. an einigen Stellen in unseren Programmen Funktionen die hundert- bis tausendfach kleine Hilfsfunktionen aufrufen um eine Datenstruktur aufzubauen. Da kann sich das dann schnell negativ auf die Code-Grösse auswirken.)</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618695</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618695</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Fri, 27 Oct 2023 19:07:02 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 19:28:06 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/13960">@hustbaer</a> sagte in <a href="/forum/post/2618695">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618675">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Für wirklich &quot;heisse&quot; Funktionen kann man das sicher rechtfertigen, für alle anderen ist  By-Value+Move allerdings meiner Meinung nach so simpel, dass es man schon fast ein gutes Argument braucht es nicht zu verwenden.</p>
</blockquote>
<p>Sehe ich ähnlich.</p>
<p>Wobei mir gerade ein Detail einfällt - etwas was viele vermutlich gar nicht wissen: &quot;by value&quot; (mit oder ohne move) erfordert u.U. (je nach Calling Convention) die Generierung eines dtor Aufrufs pro Funktionsaufruf. Was auch wieder etwas Overhead und vor allem Code-Bloat erzeugt. Das ist in den meisten Fällen vermutlich auch egal - aber es gibt Spezialfälle wo es hilfreich sein kann wenn man das weiss. (Wir haben z.B. an einigen Stellen in unseren Programmen Funktionen die hundert- bis tausendfach kleine Hilfsfunktionen aufrufen um eine Datenstruktur aufzubauen. Da kann sich das dann schnell negativ auf die Code-Grösse auswirken.)</p>
</blockquote>
<p>Ja, da hast du recht. Real-existierende Software kann echt ne Bitch sein - warum wundert es mich nicht, dass es auch hier wieder einige Sonderfälle geben kann, die einem eventuell einen Strich durch die Rechnung machen? <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f601.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--beaming_face_with_smiling_eyes"
      title=":beaming_face_with_smiling_eyes:"
      alt="😁"
    /></p>
<p>Ich habe ja eigentlich die Hoffnung, dass der Destruktor eines Objekts, von dem &quot;gemoved&quot; wurde in den meisten Fällen einen leeren Branch nimmt, der Optimizer das auch erkennen kann und letztendlich diesen leeren Branch in reines Wohlgefallen &quot;inlined&quot;. Aber man weiss nie, bevor man sich nicht den generierten Code angesehen hat. Danke für den Hinweis. Es ist völlig klar, dass der Destruktor aufgerufen werden muss (zumindest &quot;as-if&quot;), aber ich hatte das hierbei tatsächlich nicht auf dem Schirm.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618698</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618698</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Fri, 27 Oct 2023 19:28:06 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 19:40:29 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618698">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Es ist völlig klar, dass der Destruktor aufgerufen werden muss (zumindest &quot;as-if&quot;), aber ich hatte das hierbei tatsächlich nicht auf dem Schirm.</p>
</blockquote>
<p>Naja, es ist abhängig von der Calling-Convention wer den Dtor aufruft. Bei MSVC macht es die aufgerufene Funktion, bei GCC macht es der Aufrufer. (Was GCC übrigens auch dazu zwingt das Objekt im Speicher zu übergeben - auch wenn es klein genug dafür wäre in Registern übergeben zu werden.)</p>
<p>C++</p>
<pre><code class="language-cpp">struct Foo {
    Foo(int f) : f{f} {}
    ~Foo();
    int f;
};

void fun(Foo f);

void test() {
    fun(42);
}
</code></pre>
<p>MSVC</p>
<pre><code class="language-text">void test(void) PROC ; test, COMDAT
  mov ecx, 42 ; 0000002aH
  jmp void fun(Foo) ; fun
void test(void) ENDP ; test
</code></pre>
<p>GCC</p>
<pre><code class="language-text">test():
  push rbx
  sub rsp, 16
  lea rdi, [rsp+12]
  mov DWORD PTR [rsp+12], 42
  call fun(Foo)
  lea rdi, [rsp+12]
  call Foo::~Foo() [complete object destructor]
  add rsp, 16
  pop rbx
  ret
  mov rbx, rax
  jmp .L2
test() [clone .cold]:
.L2:
  lea rdi, [rsp+12]
  call Foo::~Foo() [complete object destructor]
  mov rdi, rbx
  call _Unwind_Resume
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2618702</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618702</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Fri, 27 Oct 2023 19:40:29 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 19:47:41 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/13960">@hustbaer</a> sagte in <a href="/forum/post/2618702">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618698">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Es ist völlig klar, dass der Destruktor aufgerufen werden muss (zumindest &quot;as-if&quot;), aber ich hatte das hierbei tatsächlich nicht auf dem Schirm.</p>
</blockquote>
<p>Naja, es ist abhängig von der Calling-Convention wer den Dtor aufruft. Bei MSVC macht es die aufgerufene Funktion, bei GCC macht es der Aufrufer. (Was GCC übrigens auch dazu zwingt das Objekt im Speicher zu übergeben - auch wenn es klein genug dafür wäre in Registern übergeben zu werden.)</p>
</blockquote>
<p>DAS war mir tatsächlich völlig unbekannt. Meine Kenntnis von Calling Conventions beschränkt sich nur darauf wie Argumente übergeben werden und wer den Stack aufräumt. Destruktoren oder gar Dinge wie Exception Handling gehören nicht dazu. Danke für den Hinweis <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618703</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618703</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Fri, 27 Oct 2023 19:47:41 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Fri, 27 Oct 2023 20:26:27 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/31711">@Finnegan</a> sagte in <a href="/forum/post/2618703">Member im Konstruktor per Move oder Copy initialisieren?</a>:</p>
<blockquote>
<p>Meine Kenntnis von Calling Conventions beschränkt sich nur darauf wie Argumente übergeben werden</p>
</blockquote>
<p>Was ja auch davon beeinflusst wird. Bei GCC, wenn so ein kleines Objekt keinen Dtor hat, dann wird es in Registern übergeben. Wenn es allerdings einen Dtor hat, dann muss es im Speicher übergeben werden. Weil der Aufrufer ja die Änderungen im Objekt sehen muss, damit er es korrekt zerstören kann.</p>
<p>Und was Optimierungen angeht: MSVC kann dadurch auch oft besser optimieren. Wenn ein Objekt immer &quot;konsumiert&quot; wird und der Dtor daher nichts tun würde, dann kann MSVC den manchmal wirklich wegoptimieren. z.B.:</p>
<pre><code class="language-cpp">#include &lt;string&gt;

struct Foo {
    Foo(std::string s);
    std::string s;
};

Foo::Foo(std::string s) : s(std::move(s)) {}
</code></pre>
<p>=&gt;</p>
<pre><code class="language-text">s$GSCopy$ = 0
this$GSCopy$ = 0
this$ = 32
s$ = 40
Foo::Foo(std::basic_string&lt;char,std::char_traits&lt;char&gt;,std::allocator&lt;char&gt; &gt;) PROC ; Foo::Foo, COMDAT
$LN60:
  sub rsp, 24
  xor eax, eax
  mov QWORD PTR this$GSCopy$[rsp], rcx
  xorps xmm0, xmm0
  mov QWORD PTR s$GSCopy$[rsp], rdx
  movups XMMWORD PTR [rcx], xmm0
  mov QWORD PTR [rcx+16], rax
  mov QWORD PTR [rcx+24], rax
  movups xmm0, XMMWORD PTR [rdx]
  movups XMMWORD PTR [rcx], xmm0
  movups xmm1, XMMWORD PTR [rdx+16]
  movups XMMWORD PTR [rcx+16], xmm1
  mov QWORD PTR [rdx+24], 15
  mov QWORD PTR [rdx+16], rax
  mov BYTE PTR [rdx], al
  mov rax, rcx
  mov QWORD PTR [rdx+24], 15
  add rsp, 24
  ret 0
Foo::Foo(std::basic_string&lt;char,std::char_traits&lt;char&gt;,std::allocator&lt;char&gt; &gt;) ENDP ; Foo::Foo
</code></pre>
<p>Ist jetzt nicht super übersichtlich, aber man kann denke ich gut sehen dass hier keine Operation zum Freigeben von Speicher vorhanden ist - und auch kein &quot;non-inline&quot; Funktionsaufruf der das indirekt machen könnte. D.h. der <code>std::string</code> Dtor wurde erfolgreich wegoptimiert. Das coole dabei ist, dass dazu kein Inlining von <code>Foo::Foo</code> selbst erforderlich ist - es muss nur der Code der von <code>Foo::Foo</code> aufgerufen wird inlined werden.</p>
<p>Bei GCC und Clang wäre dagegen erforderlich dass <code>Foo::Foo</code> selbst inline in den Aufrufer erweitert wird. Allerdings habe ich mit GCC/Clang noch kein nicht-triviales Beispiel zusammengebracht wo sie da auch wirklich den Dtor wegoptimieren würden. Selbst bei dem einfachen Beispiel mit einem einzigen <code>std::string</code> generieren GCC und Clang Code für den Aufruf des Dtor.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618705</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618705</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Fri, 27 Oct 2023 20:26:27 GMT</pubDate></item><item><title><![CDATA[Reply to Member im Konstruktor per Move oder Copy initialisieren? on Sat, 28 Oct 2023 16:47:17 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/13960">@hustbaer</a> Puh, das ist ja nicht so toll in diesen Fällen. Gerade wenn es bei kleinen Objekten relavant ist, die irgendwelche Ressourcen wie Speicher halten (sowas wie Länge+Pointer). Gerade bei denen sagte mir meine Intuition bisher, dass die sich besonders effizient moven lassen (was für die Move-Operation selbst ja auch stimmt). Ein <code>struct { int data [100]; }</code> wird das Destruktor-Problem nicht haben, da es ohnehin im Speicher übergeben werden muss. Dafür ist bei dem ein Move aber auch herzlich sinnlos (und de facto immer eine Kopie).</p>
<p>Ich frage mich ob diese Konvention von GCC an anderer Stelle irgendwelche Vorteile hat oder ob die einfach nur historisch gewachsen ist und man jetzt mit der Philosophie des über lange Zeit stabilen ABI festgenagelt ist. Mir persönlich wäre das zumindest nicht wichtig - ich gehe ohnehin immer davon aus, dass ich gerade für C++ auch alle Bibliotheken mit dem selben Compiler und den selben Flags kompilieren muss. Meiner Erfahrung nach ändert sich da gerade mit MSVC öfter mal was. Wenn ich Kompatibiliät will, baue ich ein C-Interface dazu (z.B. &quot;Hourglass-Style&quot;: C++ Lib &lt;-&gt; C API &lt;-&gt; C++ Header). Aber ich ich entwickle auch meist an in sich abgeschlossenen Projekten außerhalb der Linux-Paketmanager, wo alle Libs mit dabei sind.</p>
<p>Danke nochmal, wieder was gelernt <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2618713</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2618713</guid><dc:creator><![CDATA[Finnegan]]></dc:creator><pubDate>Sat, 28 Oct 2023 16:47:17 GMT</pubDate></item></channel></rss>