<?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[Rückgabeeffizienz&#x2F;RVO]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich habe gerade zum 1. Mal was zu diesem RVO/NVRO gelesen, aber bin mir noch nicht sicher ob ich das alles verstanden habe. Hier 2 von mir geschriebene Funktionen:</p>
<pre><code class="language-cpp">// Variante 1
template &lt;class T&gt;
Vector3&lt;T&gt;&amp; Vec3Normalize1(Vector3&lt;T&gt;&amp; out, const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		out = v * invLength;
	}
	else
		out = Vector3&lt;T&gt;::ZERO;

	return out;
}

// Variante 2
template &lt;class T&gt;
Vector3&lt;T&gt; Vec3Normalize2(const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		return Vector3&lt;T&gt;(v * invLength);
	}
	return Vector3&lt;T&gt;::ZERO;
}
</code></pre>
<p>Vec3Normalize1 gibt das Ergebnis im 1. Parameter zurück (und returnt den 1. Parameter als Referenz, damit Function Chaining möglich ist) und Vec3Normalize2 legt ein lokales Objekt an und gibt dieses zurück.</p>
<p>Meine Fragen: Greift bei Variante 2 RVO? Welche der 2 Funktionen ist effizienter/schneller?</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/247231/rückgabeeffizienz-rvo</link><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 15:37:55 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/247231.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 08 Aug 2009 00:49:36 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 00:49:36 GMT]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich habe gerade zum 1. Mal was zu diesem RVO/NVRO gelesen, aber bin mir noch nicht sicher ob ich das alles verstanden habe. Hier 2 von mir geschriebene Funktionen:</p>
<pre><code class="language-cpp">// Variante 1
template &lt;class T&gt;
Vector3&lt;T&gt;&amp; Vec3Normalize1(Vector3&lt;T&gt;&amp; out, const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		out = v * invLength;
	}
	else
		out = Vector3&lt;T&gt;::ZERO;

	return out;
}

// Variante 2
template &lt;class T&gt;
Vector3&lt;T&gt; Vec3Normalize2(const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		return Vector3&lt;T&gt;(v * invLength);
	}
	return Vector3&lt;T&gt;::ZERO;
}
</code></pre>
<p>Vec3Normalize1 gibt das Ergebnis im 1. Parameter zurück (und returnt den 1. Parameter als Referenz, damit Function Chaining möglich ist) und Vec3Normalize2 legt ein lokales Objekt an und gibt dieses zurück.</p>
<p>Meine Fragen: Greift bei Variante 2 RVO? Welche der 2 Funktionen ist effizienter/schneller?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757210</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757210</guid><dc:creator><![CDATA[tomate~]]></dc:creator><pubDate>Sat, 08 Aug 2009 00:49:36 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 01:39:08 GMT]]></title><description><![CDATA[<p>tomate~ schrieb:</p>
<blockquote>
<p>Welche der 2 Funktionen ist effizienter/schneller?</p>
</blockquote>
<p>kannst du nur ausprobieren</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757218</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757218</guid><dc:creator><![CDATA[fs]]></dc:creator><pubDate>Sat, 08 Aug 2009 01:39:08 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 07:34:00 GMT]]></title><description><![CDATA[<p>tomate~ schrieb:</p>
<blockquote>
<pre><code class="language-cpp">// Variante 2
template &lt;class T&gt;
Vector3&lt;T&gt; Vec3Normalize2(const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		return Vector3&lt;T&gt;(v * invLength);
	}
	return Vector3&lt;T&gt;::ZERO;
}
</code></pre>
</blockquote>
<p>Was liefert denn der Ausdruck v*invLength? Wenn das schon ein Vector3&lt;T&gt; ist, dann kannst Du dir das Vector3&lt;T&gt;() drum herum sparen. Denn Du legst von dem Ergebnis des Ausdrucks ja noch eine Kopie an. Der Compiler darf auch diese Extra-Kopie wegoptimieren. Sie scheint aber dennoch unnötig zu sein. Das, was &quot;Vector3&lt;T&gt;(v * invLength)&quot; aber erzeugt ist ein temporäres Objekt für welches man RVO anwenden kann. Compiler tun dies wahrscheinlich.</p>
<p>Mich stören 2 Dinge an dem Code:<br />
1. Du benutzt float für length und invLength<br />
2. Vector3&lt;T&gt;::ZERO ist irgendwie überflüssig.</p>
<p>So hätte ich es gemacht:</p>
<pre><code class="language-cpp">Vector3&lt;T&gt;&amp; Vector3&lt;T&gt;::equals_normnalize() // nicht-statische Elementfunktion
{
  using std::sqrt;
  T const l2 = x*x+y*y+z*z;
  if (l2&gt;0) {
    T const inv = sqrt(1/l2);
    x *= inv;
    y *= inv;
    z *= inv;
  }
  return *this;
}

...

template&lt;typename T&gt;
Vector3&lt;T&gt; normalize(Vector3&lt;T&gt; const&amp; x) // freie Funktion
{
  Vector3&lt;T&gt; result (x);
  result.equals_normalize();
  return result; // NRVO anwendbar
}
</code></pre>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757230</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757230</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 07:34:00 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 08:03:52 GMT]]></title><description><![CDATA[<p>sowsa sollte man in assembler machen, das lahme an dem code da oben sind natürlich diese ganzen c++ klassen mit ihren zig konstruktoren die da aufgerufen werden und objekte anlegen und zerstören nur um nen vektor zu normalisieren :roller:</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757234</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757234</guid><dc:creator><![CDATA[tsukuhime]]></dc:creator><pubDate>Sat, 08 Aug 2009 08:03:52 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 09:19:11 GMT]]></title><description><![CDATA[<p>Einen benutzerdefinierten Copy-Konstruktor braucht man hier genauso wenig wie einen Destruktor -- man kann und <strong>sollte</strong> sie also weglassen und dem Compiler überlassen. Wenn Du dann für T noch so etwas wie double oder float einsetzt, muss beim Kopieren <strong>gar nichts</strong> &quot;aufgerufen&quot; werden.</p>
<p>Experiment: Ich übersetze das hier</p>
<pre><code class="language-cpp">#include &lt;cmath&gt;

class foo
{
	float x, y, z;
public:
	explicit foo(float x=0, float y=0, float z=0)
	:x(x), y(y), z(z) {}

	foo&amp; equals_normalize()
	{
		using std::sqrt;
		float f = sqrt(1/(x*x+y*y+z*z));
		x*=f;
		y*=f;
		z*=f;
		return *this;
	}
};

inline foo normalize(foo const&amp; x)
{
	foo r = x;
	r.equals_normalize();
	return r;
}

void blah(foo t);

void dings(foo t)
{
	blah(normalize(t));
}
</code></pre>
<p>mit <code>g++ -O3 -S -o assembler.s</code> und schau Dir das Ergebnis per <code>cat assembler.s | c++filt</code> an. Hier ist der Abschnitt für dings():</p>
<pre><code>dings(foo):
.LFB97:
	pushl	%ebp
.LCFI0:
	movl	%esp, %ebp
.LCFI1:
	subl	$16, %esp
.LCFI2:
	flds	8(%ebp)
	flds	12(%ebp)
	flds	16(%ebp)
	fld	%st(2)
	fmul	%st(3), %st
	fld	%st(2)
	fmul	%st(3), %st
	faddp	%st, %st(1)
	fld	%st(1)
	fmul	%st(2), %st
	faddp	%st, %st(1)
	fdivrs	.LC0
	fsqrt
	fmul	%st, %st(1)
	fxch	%st(1)
	fstps	-4(%ebp)
	fmul	%st, %st(1)
	fxch	%st(1)
	fstps	-8(%ebp)
	fmulp	%st, %st(1)
	fstps	-12(%ebp)
	movl	-12(%ebp), %eax
	movl	%eax, 8(%ebp)
	movl	-8(%ebp), %eax
	movl	%eax, 12(%ebp)
	movl	-4(%ebp), %eax
	movl	%eax, 16(%ebp)
	leave
	jmp	blah(foo)
</code></pre>
<p>Ich sehe hier kein <code>call</code> für einen Copy-C'tor Aufruf.</p>
<p>So, tsukuhime, Du meinst also, Du kannst besseren Assembler-Code generieren, was? :p</p>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757266</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757266</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 09:19:11 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 09:37:09 GMT]]></title><description><![CDATA[<p>Man muss nicht mal Assembler benutzen, wenn man den MMX/SSE/3Dnow! Befehlssatz ausnutzen will. Dazu unterstützen die meisten Compiler an MMX/SSE/3Dnow! angelehnte <a href="http://en.wikipedia.org/wiki/Intrinsic_function" rel="nofollow">&quot;intrinsics&quot;</a>.</p>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757277</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757277</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 09:37:09 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 14:27:23 GMT]]></title><description><![CDATA[<p>Danke für deine Antworten Sebastian Pizer.<br />
Ein paar Sachen sind mir nicht klar:<br />
Du schreibst in deinem Code: T const inv = sqrt(1/l2);<br />
1. Wenn ich jetzt einen Vector3&lt;int&gt; anlege, gibt das doch einen Compiler-Error, oder?</p>
<p>2. Du schreibst wenn es geht sollte man keinen CopyCtor und Dtor selber schreiben. Für mich hört sich das fast so an, als sei das eine notwendige Bedigung, damit RVO und NRVO greift. Ist das so? Wird NIE eine RVO/NRVO gemacht, wenn ich selber einen CopyCtor oder Dtor schreibe?</p>
<p>3. In meinem Normalize2 habe ich ja 2 returns und die geben unterschiedliche Objekte zurück. Ich dachte in so einem Fall ist NRVO (und RVO) unmöglich? Es klang bei dir so, als sollte es dennoch gehen?</p>
<p>4. Ich habe Normalize2 etwas umgeschrieben, so dass es nur noch ein return hat:</p>
<pre><code class="language-cpp">template &lt;class T&gt;
Vector3&lt;T&gt; Vec3Normalize2(const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	Vector3&lt;T&gt; ret(Vector3&lt;T&gt;::ZERO);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		ret = v * invLength;
	}
	return ret;
}

// Aufruf A:
Vec3f v(2,3,4);
Vec3f v2 = Vec3Normalize2(v);

// Aufruf B:
Vec3f v(2,3,4);
Vec3f v2;
v2 = Vec3Normalize2(v);
</code></pre>
<p>Wenn ich die Funktion wie in &quot;Aufruf A&quot; aufrufe, dann ist die Ausgabe:</p>
<blockquote>
<p>Ctor(x,y,z)<br />
CopyCtor()<br />
Ctor(x,y,z)<br />
Dtor()</p>
</blockquote>
<p>NRVO scheint also zu greifen. Rufe ich den Code hingegen wie in &quot;Aurfuf B&quot; auf, so ergibt die Ausgabe:</p>
<blockquote>
<p>Ctor(x,y,z)<br />
Ctor()<br />
CopyCtor()<br />
Ctor(x,y,z)<br />
Dtor()<br />
Dtor()</p>
</blockquote>
<p>Ich finde das ist eine stattliche Anzahl von Aufrufen, nur für das Normalisieren eines Vektors!<br />
Die Frage deshalb: Sollte ich nicht besser alle Rückgaben als Referenz im 1. Parameter zurückgeben? Also lieber das: &quot;void MatrixInvert(m, m2)&quot; anstatt von &quot;m=MatrixInvert(m2)&quot;?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757412</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757412</guid><dc:creator><![CDATA[tomate~]]></dc:creator><pubDate>Sat, 08 Aug 2009 14:27:23 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 15:41:52 GMT]]></title><description><![CDATA[<p>1. ausprobieren<br />
2. nö, ist keine bedingung<br />
3. also der (2008er) msvc kann das auf jeden fall - hab ma nen bsp gemacht, siehe ende.</p>
<p>point hatte ich gerade zur hand, weils in nem anderen thread darum ging ^^<br />
<code>__declspec(noinline)</code> ist msvc spezifisch, weil ich nicht wollte, dass er die fkt inlined - dann wär es ja klar, dass copy ctor etc nich aufgerufen werden</p>
<p>bsp.:</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;iostream&gt;

struct point
{
	float x, y;
	explicit point(float _x = 0, float _y = 0) : x(_x), y(_y)
	{
		std::cout &lt;&lt; &quot;CTor&quot; &lt;&lt; std::endl;
	}
	point(const point &amp;other) : x(other.x), y(other.y)
	{
		std::cout &lt;&lt; &quot;CopyCTor&quot; &lt;&lt; std::endl;
	}

	void swap(point &amp;other)
	{
		using std::swap;
		swap(x, other.x);
		swap(y, other.y);
	}

	point&amp; operator= (const point &amp;other)
	{
		std::cout &lt;&lt; &quot;op=&quot; &lt;&lt; std::endl;
		point tmp(other);
		swap(tmp);
		return *this;
	}

	~point()
	{
		std::cout &lt;&lt; &quot;DTor&quot; &lt;&lt; std::endl;
	}
};

std::ostream&amp; operator&lt;&lt; (std::ostream &amp;s, const point &amp;these)
{
	return s &lt;&lt; these.x &lt;&lt; ' ' &lt;&lt; these.y;
}

point __declspec(noinline) test(float a, float b)
{
	if(b == 0)
		return point(a, a);
	if(a &lt; 0)
		return point(a, b);
	else
		return point(b, a);
}

int main()
{
	float a, b;
	std::cin &gt;&gt; a &gt;&gt; b;
	std::cout &lt;&lt; test(a, b) &lt;&lt; std::endl;
	point x;
	std::cout &lt;&lt; (x = test(b, a)) &lt;&lt; std::endl;
}
</code></pre>
<p>MSVC schrieb:</p>
<blockquote>
<p>2<br />
3<br />
CTor<br />
3 2<br />
DTor<br />
CTor<br />
CTor<br />
op=<br />
CopyCTor<br />
DTor<br />
2 3<br />
DTor</p>
</blockquote>
<p>-&gt;</p>
<pre><code class="language-cpp">// Eingaben

// erste Variante:
    CTor
    //ausgabe
    DTor

//zweite Variante:
    CTor //CTor mit (0,0)
    CTor //das eigentliche objekt, welches dem alten zugewiesen wird
    op= //zuweisung
      CopyCTor //tmp wird erstellt
      //geswapt
      DTor //tmp wird zerstört
    //ausgabe
    DTor
</code></pre>
<p>PS:<br />
wieso braucht man die extra klammern hier eigtl:<br />
<code>std::cout &lt;&lt; (x = test(b, a)) &lt;&lt; std::endl;</code></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757432</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757432</guid><dc:creator><![CDATA[unskilled]]></dc:creator><pubDate>Sat, 08 Aug 2009 15:41:52 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 15:47:13 GMT]]></title><description><![CDATA[<p>tomate~ schrieb:</p>
<blockquote>
<p>Du schreibst in deinem Code: T const inv = sqrt(1/l2);<br />
1. Wenn ich jetzt einen Vector3&lt;int&gt; anlege, gibt das doch einen Compiler-Error, oder?</p>
</blockquote>
<p>Möglich. Was soll denn das Ergebnis von normalize(Vector3&lt;int&gt;(1,2,3)) sein? :p</p>
<p>tomate~ schrieb:</p>
<blockquote>
<p>2. Du schreibst wenn es geht sollte man keinen CopyCtor und Dtor selber schreiben. Für mich hört sich das fast so an, als sei das eine notwendige Bedigung, damit RVO und NRVO greift. Ist das so?</p>
</blockquote>
<p>Nein. Das hat nichts damit zu tun. Bei (N)RVO oder noch allgemeiner &quot;copy elision&quot; geht es um das Reduzieren von unnötigen Kopien. Ob man beim Kopieren einen benutzerdefinierten Konstruktor aufrufen muss oder nicht, ist egal. &quot;copy elision&quot; ist <em>gerade dann</em> interessant, wenn die Objekte nicht-triviale Copy C'tors und D'tors haben.</p>
<p>tomate~ schrieb:</p>
<blockquote>
<p>3. In meinem Normalize2 habe ich ja 2 returns und die geben unterschiedliche Objekte zurück. Ich dachte in so einem Fall ist NRVO (und RVO) unmöglich? Es klang bei dir so, als sollte es dennoch gehen?</p>
</blockquote>
<p>Bei NRVO spielt das eine Rolle, ja. Stell Dir vor, der Compiler übergibt einfach einen Zeiger als zusätzlichen Parameter, der auf die Stelle zeigt, wo das Funktionsergebnis konstruiert werden soll. Du hast keine return-Anweisung benutzt, die eine lokale Variable benennt. Im ersten Fall wird ein temporäres Objekt erzeugt. Das kann direkt dort erzeugt werden, wo der Zeiger hinzeigt (RVO). Im zweiten Fall benennst Du ein lvalue-Objekt. Das ist kein besonders interessanter Fall: Das Ergebnisobjekt wird einfach kopiert.</p>
<p>tomate~ schrieb:</p>
<blockquote>
<p>4. Ich habe Normalize2 etwas umgeschrieben, so dass es nur noch ein return hat:</p>
<pre><code class="language-cpp">template &lt;class T&gt;
Vector3&lt;T&gt; Vec3Normalize2(const Vector3&lt;T&gt;&amp; v) {
	float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);

	Vector3&lt;T&gt; ret(Vector3&lt;T&gt;::ZERO);

	if(length &gt; 0) {
		float invLength = 1.0f / length;
		ret = v * invLength;
	}
	return ret;
}

// Aufruf A:
Vec3f v(2,3,4);
Vec3f v2 = Vec3Normalize2(v);

// Aufruf B:
Vec3f v(2,3,4);
Vec3f v2;
v2 = Vec3Normalize2(v);
</code></pre>
<p>Wenn ich die Funktion wie in &quot;Aufruf A&quot; aufrufe, dann ist die Ausgabe:</p>
<pre><code>Ctor(x,y,z)
CopyCtor()
Ctor(x,y,z)
Dtor()
</code></pre>
<p>NRVO scheint also zu greifen.</p>
</blockquote>
<p>Wieso? Das &quot;CopyCtor()&quot; kommt sicherlich von Deiner Initialisierung von &quot;ret&quot; mit Vector3&lt;T&gt;::ZERO. Außerdem, was macht denn eigentlich &quot;ret = v * invLength&quot;? Überleg mal genau nach. Ich habe bisher keine Definition von operator* und/oder operator= gesehen, kann das also nicht beantworten. Es sieht aber verdächtig aus. Wenn ich raten müsste:</p>
<pre><code class="language-cpp">template&lt;typename T&gt;
Vector3&lt;T&gt; operator*(Vector3&lt;T&gt; const&amp; v, float f)
{
  return Vector3&lt;T&gt;(v.x*f, v.y*f, v.z*f);
}
</code></pre>
<p>&quot;v * invLength&quot; liefert also ein temporäres Objekt, welches per operator= dem Objekt ret zugewiesen wird (copy assignment).</p>
<blockquote>
<p>Rufe ich den Code hingegen wie in &quot;Aurfuf B&quot; auf, so ergibt die Ausgabe:</p>
<pre><code>Ctor(x,y,z)
Ctor()
CopyCtor()
Ctor(x,y,z)
Dtor()
Dtor()
</code></pre>
<p>Ich finde das ist eine stattliche Anzahl von Aufrufen, nur für das Normalisieren eines Vektors!</p>
</blockquote>
<p>Da bist Du selbst dran Schuld. Sehr wahrscheinlich sieht das so aus:</p>
<pre><code>Ctor(x,y,z)  // Konstruktion von v
-----------------------------------------------------
Ctor()       // Konstruktion von v2
-----------------------------------------------------
CopyCtor()   // Copy-Konstruktion von &quot;ret(ZERO)&quot;
-----------------------------------------------------
Ctor(x,y,z)  // Konstruktion von v*invLength (&quot;tmp1&quot;)
             // (RVO wurde angewendet)
-----------------------------------------------------
             // copy assignment: ret = tmp1
-----------------------------------------------------
Dtor()       // tmp1 wird zerstört
-----------------------------------------------------
             // Das Ergebnis von Vec3Normalize2(v) ist
             // ein neues temporäres Objekt (&quot;tmp2&quot;).
             // Hier wurde NRVO angewendet, so dass
             // tmp2 DASSELBE Objekt wie ret ist.
-----------------------------------------------------
             // copy assignment: v2 = tmp2;
-----------------------------------------------------
Dtor()       // tmp2 wird zerstört
-----------------------------------------------------
Dtor()       // v2 wird zerstört
Dtor()       // v wird zerstört
</code></pre>
<p>Code so wie der hier</p>
<pre><code class="language-cpp">Vec3f v2;
v2 = Vec3Normalize2(v);
</code></pre>
<p>ist &quot;bah pfui!&quot; <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /> weil Du hier eine Default-Initialisierung gefolgt von einem Copy-Assignment hast wobei</p>
<pre><code class="language-cpp">Vec3f v2 = Vec3Normalize2(v);
</code></pre>
<p>eine Copy-Initialisierung ist, bei der &quot;copy elision&quot; angewendet werden kann.</p>
<blockquote>
<p>Die Frage deshalb: Sollte ich nicht besser alle Rückgaben als Referenz im 1. Parameter zurückgeben? Also lieber das: &quot;void MatrixInvert(m, m2)&quot; anstatt von &quot;m=MatrixInvert(m2)&quot;?</p>
</blockquote>
<p>Für so ein &quot;Popel-Objekt&quot;, welches -- wenn Du Deine ganzen cout&lt;&lt;&quot;blah&quot;-Methoden wieder rausschmeißt -- trivial kopierbar ist sofern T trivial kopierbar ist, schadet das nur. Du gewinnst rein gar nichts damit. Du bekommst nur unleserlichen Code dadurch.</p>
<p>Bei einem &quot;fetten&quot; Matrix-Objekt, welches seine Elemente zB intern in einem std::vector&lt;double&gt; verwaltet ist das etwas anderes -- aber auch nicht mehr lange, dank C++0x und Rvalue-Referenzen.</p>
<p>Es bleibt zu hoffen, dass die Leute mit Einzug von C++0x einen etwas &quot;funktionaleren&quot; Stil an den Tag legen. Wie Howard Hinnant so schön (sinngemäß) sagte: Es gibt jetzt keinen guten Grund mehr, Referenzen für Ergebnisse zu übergeben.</p>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757436</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757436</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 15:47:13 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 15:48:55 GMT]]></title><description><![CDATA[<p>unskilled schrieb:</p>
<blockquote>
<p>PS:<br />
wieso braucht man die extra klammern hier eigtl:<br />
<code>std::cout &lt;&lt; (x = test(b, a)) &lt;&lt; std::endl;</code></p>
</blockquote>
<p>Ich würde mal vermuten weil er sonst zunächst cout &lt;&lt; x auswertet und dann dem zurückgegebenen stream einen point zuweisen will, was natürlich nicht geht.</p>
<p>Ok, das bedeutet ich kann dutzende returns haben und jedes return gibt ein anderes Objekt zurück und RVO/NRVO wird dennoch klappen? Ist das garantiert beim MSVC 2008?</p>
<p>Meine Frage:</p>
<blockquote>
<p>Ich finde das ist eine stattliche Anzahl von Aufrufen, nur für das Normalisieren eines Vektors!<br />
Die Frage deshalb: Sollte ich nicht besser alle Rückgaben als Referenz im 1. Parameter zurückgeben? Also lieber das: &quot;void MatrixInvert(m, m2)&quot; anstatt von &quot;m=MatrixInvert(m2)&quot;?</p>
</blockquote>
<p>steht übrigens noch im Raum;)</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757439</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757439</guid><dc:creator><![CDATA[tomate~]]></dc:creator><pubDate>Sat, 08 Aug 2009 15:48:55 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 15:56:00 GMT]]></title><description><![CDATA[<blockquote>
<p>Ich finde das ist eine stattliche Anzahl von Aufrufen, nur für das Normalisieren eines Vektors!</p>
</blockquote>
<p>Die Frage steht gar nicht mehr offen - weniger gehen ja nun gar nicht mehr...</p>
<p>@RVO/NRVO:<br />
imho kann kein compiler garantieren, dass das immer klappt...</p>
<p>bb</p>
<p>PS:</p>
<blockquote>
<p>Ich würde mal vermuten weil er sonst zunächst cout &lt;&lt; x auswertet und dann dem zurückgegebenen stream einen point zuweisen will, was natürlich nicht geht.</p>
</blockquote>
<p>Nö, so ists nicht... Sonst hätte die Fehlermeldung anders gelautet. Damit ich sie dir nicht vorenthalte:</p>
<p>MSVC schrieb:</p>
<blockquote>
<p>1&gt;.\main.cpp(58) : error C2678: binary '&lt;&lt;' : no operator found which takes a left-hand operand of type 'point' (or there is no acceptable conversion)<br />
1&gt; .\main.cpp(37): could be 'std::ostream &amp;operator &lt;&lt;(std::ostream &amp;,const point &amp;)'<br />
1&gt; while trying to match the argument list '(point, overloaded-function)'</p>
</blockquote>
]]></description><link>https://www.c-plusplus.net/forum/post/1757444</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757444</guid><dc:creator><![CDATA[unskilled]]></dc:creator><pubDate>Sat, 08 Aug 2009 15:56:00 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 16:02:11 GMT]]></title><description><![CDATA[<p>tomate~ schrieb:</p>
<blockquote>
<p>Meine Frage: [...] steht übrigens noch im Raum;)</p>
</blockquote>
<p>Ich hoffe, Du hast meine Antwort nicht übersehen. <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/1757447</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757447</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 16:02:11 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 16:19:36 GMT]]></title><description><![CDATA[<p>Sebastian Pizer schrieb:</p>
<blockquote>
<p>tomate~ schrieb:</p>
<blockquote>
<p>Meine Frage: [...] steht übrigens noch im Raum;)</p>
</blockquote>
<p>Ich hoffe, Du hast meine Antwort nicht übersehen. <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>
</blockquote>
<p>Hehe, nein.</p>
<p>Aalso. Ein paar Dinge finde ich seltsam die du schreibst. In meinem Beispiel &quot;Aufruf A&quot; schreibe ich, dass hier NVRO offenbar angewandt wird, was du mit &quot;Wieso?&quot; in Frage stellst. Aber im weiteren Text bestätigst du dann doch, dass es NVRO ist.<br />
Mein operator* sieht genau so aus, wie du es dir gedacht hast. Ergo kann man diese Ausgabe:</p>
<pre><code class="language-cpp">Vec3f v(2,3,4);
Vec3f v2 = Vec3Normalize2(v);

Ctor(x,y,z)  // v wird erzeugt
CopyCtor()   //  Vector3&lt;T&gt; ret(Vector3&lt;T&gt;::ZERO);
Ctor(x,y,z)  // das ist das Objekt, das in operator* erzeugt wird
Dtor() // nach operator= wird das temp. Objekt erzeugt.
</code></pre>
<p>genau so erklären wie du es beschrieben hast (siehe Kommentare)<br />
Aber es gibt eben KEINEN weiteren CopyCtor und Dtor, was ja darauf hinweist, DASS NVRO angewandt wurde. Richtig?</p>
<p>Ich habe jetzt mal deinen Ansatz genommen, sprich:</p>
<pre><code class="language-cpp">template &lt;class T&gt; 
Vector3&lt;T&gt; Vec3Normalize(const Vector3&lt;T&gt;&amp; v) { 
	Vector3&lt;T&gt; ret(v);
	ret.normalize();
	return ret;
}

// Aufruf:
Vec3f v(2,3,4);
Vec3f v2 = Vec3Normalize(v);

// Ausgabe:
Ctor(x,y,z)
CopyCtor()
</code></pre>
<p>Das ist jetzt so ziemlich das Optimum, oder?</p>
<p>Was mich als &quot;Anfänger&quot; einfach verunsichert: Überall höre ich, dass der Compiler (N)RVO benutzen KANN (und auch nur dann, wenn ich passenden Code schreibe). Ich will aber nicht ständig für den Compiler programmieren, so dass er (N)RVO betreiben kann. Außerdem will ich auch nicht vor Zeilen a la</p>
<pre><code class="language-cpp">Matrix m;
...
m = MatrixRotY(45);
</code></pre>
<p>Angst haben müssen.<br />
Ich weiß auch nicht wie böse so eine Zeile ist (Meine Matrix hat 16 floats).<br />
Würdet ihr bei einer Matrix mit 16 floats ebenfalls die Ergebnisse per value zurückgeben?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757455</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757455</guid><dc:creator><![CDATA[tomate~]]></dc:creator><pubDate>Sat, 08 Aug 2009 16:19:36 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 16:51:30 GMT]]></title><description><![CDATA[<p>tomate~ schrieb:</p>
<blockquote>
<p>Aalso. Ein paar Dinge finde ich seltsam die du schreibst. In meinem Beispiel &quot;Aufruf A&quot; schreibe ich, dass hier NVRO offenbar angewandt wird, was du mit &quot;Wieso?&quot; in Frage stellst.</p>
</blockquote>
<p>Entschuldige. Ich hatte &quot;offenbar nicht angewandt&quot; gelesen.</p>
<p>tomate~ schrieb:</p>
<blockquote>
<p>...</p>
<pre><code>// Ausgabe:
Ctor(x,y,z)
CopyCtor()
</code></pre>
<p>Das ist jetzt so ziemlich das Optimum, oder?</p>
</blockquote>
<p>Ja, besser geht's nicht, ne? <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="🙂"
    /><br />
Vorteil: Du kannst mit &quot;.normalize()&quot; einen Vektor &quot;in-place&quot; normalisieren. 2 Funktionen sind hier also recht praktisch. Manchmal hat man schon einen Vektor, den man in-place normalisieren will und manchmal will man eine normalisierte Kopie haben. Allerdings sollte die Elementfunktion &quot;.normalize&quot; gut dokumentiert sein. Verändert sie Objekt und liefert eine Referenz auf sich oder erzeugt sie eine normalisierte Kopie und verändert das Objekt nicht? Deswegen hatte ich einen Namen gewählt, der das verdeutlichen sollte (&quot;normalize_equals&quot; in Anlehnung an die Operatoren +=, -=, *=, /=, ...). operator+ definiere ich auch immer gerne mittels operator+= ...</p>
<pre><code class="language-cpp">inline foo operator+(foo const&amp; a, foo const&amp; b)  
  {
    foo r (a); r += b; return r; // setzt auf NRVO  
  }
</code></pre>
<p>tomate~ schrieb:</p>
<blockquote>
<p>Was mich als &quot;Anfänger&quot; einfach verunsichert: Überall höre ich, dass der Compiler (N)RVO benutzen KANN (und auch nur dann, wenn ich passenden Code schreibe). Ich will aber nicht ständig für den Compiler programmieren, so dass er (N)RVO betreiben kann. Außerdem will ich auch nicht vor Zeilen a la</p>
<pre><code class="language-cpp">Matrix m;
...
m = MatrixRotY(45);
</code></pre>
<p>Angst haben müssen.<br />
Ich weiß auch nicht wie böse so eine Zeile ist (Meine Matrix hat 16 floats).<br />
Würdet ihr bei einer Matrix mit 16 floats ebenfalls die Ergebnisse per value zurückgeben?</p>
</blockquote>
<p>Die Sorgen und Einwände sind berechtigt. Ich verlasse mich auch nicht 100%ig darauf, dass (N)RVO durchgeführt wird. In den Fällen, wo es einen richtig großen Unterschied macht, setzte ich NICHT darauf. Ich erwarte es aber trotzdem von einem guten Compiler.</p>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757471</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757471</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 16:51:30 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 17:06:42 GMT]]></title><description><![CDATA[<p>Schon mal vielen Dank für deine Antworten. Sie waren sehr hilfreich! <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>
<p>Das Problem ist halt nur, dass wenn ich jetzt &quot;kleine&quot; Objekte (dazu zähle ich einen Vector3) per Value zurückgebe, aber eine Matrix by reference per Parameter, dann ist das inkonsistent und damit auch unschön:</p>
<pre><code class="language-cpp">Vec3f v(2,3,4);
Vec3f v2 = Vec3Normalize(v);  // Rückgabe per value

Matrix4 m ;
Mat4RotationY(m, 90);   // Rückgabe per parameter
</code></pre>
<p>Jetzt muss der Anwender (und ich) meines Codes immer genau hinschauen, wie jede Funktion funktioniert. Würde ich hingegen IMMER non-built-in Typen im 1. Parameter per Reference zurückgeben, wäre es vllt syntaktisch nicht sonderlich schön, aber einheitlich:</p>
<pre><code class="language-cpp">Vec3f v(2,3,4);
Vec3f v2;
Vec3Normalize(v2, v);  

Matrix4 m;
Mat4RotationY(m, 90);
</code></pre>
<p>Ich denke du weißt was ich meine. Ich kann mich einfach nicht entscheiden, wie ich es machen soll <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f61e.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--disappointed_face"
      title=":("
      alt="😞"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757479</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757479</guid><dc:creator><![CDATA[tomate~]]></dc:creator><pubDate>Sat, 08 Aug 2009 17:06:42 GMT</pubDate></item><item><title><![CDATA[Reply to Rückgabeeffizienz&#x2F;RVO on Sat, 08 Aug 2009 17:26:53 GMT]]></title><description><![CDATA[<p>Du könntest es so machen:</p>
<pre><code class="language-cpp">struct matrix4f {
  float elements[4][4];
};

void make_rotation(vec3f axis, float angle, matrix4f &amp; out);

inline matrix4f make_rotation(vec3f axis, float angle) {
  matrix4f m;
  make_rotation(axis,angle,m);
  return m; // setzt auf NRVO
}
</code></pre>
<p>Wenn NRVO hier nicht angewendet wird, ist das kein Beinbruch. Man hat dann immer noch die Wahl, statt</p>
<pre><code class="language-cpp">matrix4f m = make_rotation(......);
</code></pre>
<p>einfach</p>
<pre><code class="language-cpp">matrix4f m; make_rotation(......,m);
</code></pre>
<p>zu schreiben. Das würde ich aber erst dann machen, wenn ich festgestellt habe, dass diese Änderung auch etwas bringt.</p>
<p>Wenn Du schon ein Matrix-Objekt hast, dann solltest Du</p>
<pre><code class="language-cpp">make_rotation(......,m);
</code></pre>
<p>statt</p>
<pre><code class="language-cpp">m = make_rotation(......);
</code></pre>
<p>schreiben, weil Du damit -- unabhängig von (N)RVO -- eine Kopie sparst.</p>
<p>Gruß,<br />
SP</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1757499</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1757499</guid><dc:creator><![CDATA[Sebastian Pizer]]></dc:creator><pubDate>Sat, 08 Aug 2009 17:26:53 GMT</pubDate></item></channel></rss>