<?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[Sind rekursive Templatefunktionen wohlgeformt?]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich versuche in letzter Zeit, meine Templatekonstrukte einfacher lesbar zu machen, indem ich sie viel stärker wie normalen Code aussehen lasse. Hier mal ein simples Beispiel:</p>
<pre><code class="language-cpp">//Foo ist irgendein typ
struct Foo{};

//Bar ist ein beliebiger Wrapper der Teil eines expression templates ist
template&lt;class T&gt;
struct Bar{
    Bar(T const&amp; t);
    T const&amp; get()const;
};

template&lt;class T&gt;
Bar&lt;T&gt; makeBar(T const&amp; t){
    return Bar&lt;T&gt;(t);
}

//nun möchte ich gerne zählen, ob die Anzahl der &quot;Bar&quot;s 
//in einem template Ausdruck gerade ist
template&lt;class T&gt;
bool evenBars(Bar&lt;T&gt; const&amp; bar){
    return !evenBars(bar.get());
}
bool evenBars(Foo const&amp; foo){
    return true;//0 ist gerade
}

//test:
Foo foo;
std::cout&lt;&lt;evenBars(makeBar(makeBar(foo)));//ausgabe true
</code></pre>
<p>Anstatt irgendeinen Klassentyp zu verwenden, bei dem ich dann mit statischen Konstanten den Wert abfrage, möchte ich eine einfache Funktion haben.</p>
<p>Das war das Vorgeplänkel. Nun würde ich das gerne in Templatelogik einbauen:</p>
<pre><code class="language-cpp">template&lt;class T&gt;
void myFunc(T const&amp; t){
    //ist die Anzahl der &quot;Bar&quot; in T ungerade, funktioniert myFunc nicht. 
    //Also: gerade machen
    if(!evenBars(t)){
        myFunc(makeBar(t));
        return;
    }
    //okay, Anzahl der Bars ist gerade, hier jetzt irgendwas tolles machen
}
</code></pre>
<p>Die Frage ist: ist der Code wohlgeformt? Dies ist ein rekursiver Aufruf einer Templatefunktion, bei der die Typen der Argumente sich ändern. Der Compiler hat also zwei Möglichkeiten:</p>
<p>1. Er instantiiert myFunc&lt;T&gt;. Er analysiert das if() und sieht, dass es entweder konstant true oder false ist. wenn es false ist, optimiert er den Zweig komplett heraus, ohne das Innere weitere zu betrachten. In diesem Fall terminiert das Metaprogramm nach maximal 2 Schritten.</p>
<p>2. Der Compiler instantiiert myFunc&lt;T&gt; komplett ohne die Bedingung des if() zu betrachten. Das heißt er wird als nächstes myFunc&lt;Bar&lt;T&gt; &gt; instantiieren unabhängig des Wertes von evenBars&lt;T&gt;. In der Instantiierung wird er wieder myFunc&lt;Bar&lt;Bar&lt;T&gt; &gt; &gt; instantiieren -&gt; Das Metaprogramm terminiert nicht und der Compiler gelangt in eine Endlosschleife.</p>
<p>Ich bin mir bewusst, dass ich das alles mit Typlogik und verschiedenen Funktion zur besten Zufriedenheit lösen kann. Aber das ist unglaublich viel Aufwand für eine eigentlich einfache Sache.</p>
<p>Sagt der Standard etwas dazu aus, wie der Compiler in einem solchen Fall vorgehen muss? Oder sagt er sogar, dass dieses Programm nicht wohlgeformt ist?</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/297642/sind-rekursive-templatefunktionen-wohlgeformt</link><generator>RSS for Node</generator><lastBuildDate>Fri, 14 Aug 2026 08:24:40 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/297642.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 03 Jan 2012 20:34:16 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 20:34:16 GMT]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich versuche in letzter Zeit, meine Templatekonstrukte einfacher lesbar zu machen, indem ich sie viel stärker wie normalen Code aussehen lasse. Hier mal ein simples Beispiel:</p>
<pre><code class="language-cpp">//Foo ist irgendein typ
struct Foo{};

//Bar ist ein beliebiger Wrapper der Teil eines expression templates ist
template&lt;class T&gt;
struct Bar{
    Bar(T const&amp; t);
    T const&amp; get()const;
};

template&lt;class T&gt;
Bar&lt;T&gt; makeBar(T const&amp; t){
    return Bar&lt;T&gt;(t);
}

//nun möchte ich gerne zählen, ob die Anzahl der &quot;Bar&quot;s 
//in einem template Ausdruck gerade ist
template&lt;class T&gt;
bool evenBars(Bar&lt;T&gt; const&amp; bar){
    return !evenBars(bar.get());
}
bool evenBars(Foo const&amp; foo){
    return true;//0 ist gerade
}

//test:
Foo foo;
std::cout&lt;&lt;evenBars(makeBar(makeBar(foo)));//ausgabe true
</code></pre>
<p>Anstatt irgendeinen Klassentyp zu verwenden, bei dem ich dann mit statischen Konstanten den Wert abfrage, möchte ich eine einfache Funktion haben.</p>
<p>Das war das Vorgeplänkel. Nun würde ich das gerne in Templatelogik einbauen:</p>
<pre><code class="language-cpp">template&lt;class T&gt;
void myFunc(T const&amp; t){
    //ist die Anzahl der &quot;Bar&quot; in T ungerade, funktioniert myFunc nicht. 
    //Also: gerade machen
    if(!evenBars(t)){
        myFunc(makeBar(t));
        return;
    }
    //okay, Anzahl der Bars ist gerade, hier jetzt irgendwas tolles machen
}
</code></pre>
<p>Die Frage ist: ist der Code wohlgeformt? Dies ist ein rekursiver Aufruf einer Templatefunktion, bei der die Typen der Argumente sich ändern. Der Compiler hat also zwei Möglichkeiten:</p>
<p>1. Er instantiiert myFunc&lt;T&gt;. Er analysiert das if() und sieht, dass es entweder konstant true oder false ist. wenn es false ist, optimiert er den Zweig komplett heraus, ohne das Innere weitere zu betrachten. In diesem Fall terminiert das Metaprogramm nach maximal 2 Schritten.</p>
<p>2. Der Compiler instantiiert myFunc&lt;T&gt; komplett ohne die Bedingung des if() zu betrachten. Das heißt er wird als nächstes myFunc&lt;Bar&lt;T&gt; &gt; instantiieren unabhängig des Wertes von evenBars&lt;T&gt;. In der Instantiierung wird er wieder myFunc&lt;Bar&lt;Bar&lt;T&gt; &gt; &gt; instantiieren -&gt; Das Metaprogramm terminiert nicht und der Compiler gelangt in eine Endlosschleife.</p>
<p>Ich bin mir bewusst, dass ich das alles mit Typlogik und verschiedenen Funktion zur besten Zufriedenheit lösen kann. Aber das ist unglaublich viel Aufwand für eine eigentlich einfache Sache.</p>
<p>Sagt der Standard etwas dazu aus, wie der Compiler in einem solchen Fall vorgehen muss? Oder sagt er sogar, dass dieses Programm nicht wohlgeformt ist?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163431</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163431</guid><dc:creator><![CDATA[otze]]></dc:creator><pubDate>Tue, 03 Jan 2012 20:34:16 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 20:51:30 GMT]]></title><description><![CDATA[<p>otze schrieb:</p>
<blockquote>
<p>wenn es false ist, optimiert er den Zweig komplett heraus, ohne das Innere weitere zu betrachten.</p>
</blockquote>
<p>Träumst du? Ein static_if gibt es noch nicht in C++.</p>
<p>Wenn du C++11 verwendest, musst du lediglich eine Überladung von myFunc hinzufügen (dem Rest muss nur noch ein constexpr verpasst werden), das hält sich IMHO noch in Grenzen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163436</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163436</guid><dc:creator><![CDATA[dream0r]]></dc:creator><pubDate>Tue, 03 Jan 2012 20:51:30 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 21:27:18 GMT]]></title><description><![CDATA[<p>otze schrieb:</p>
<blockquote>
<p>Oder sagt er sogar, dass dieses Programm nicht wohlgeformt ist?</p>
</blockquote>
<p>nein, der Standard sagt, dass das Ergebnis unbegrenzter impliziter Instantiierung undefiniert ist... also im Prinzip ja (14.7.1/15) <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="😉"
    /></p>
<p>C++11 schrieb:</p>
<blockquote>
<p>14.7.1 Implicit instantiation [temp.inst]<br />
[...]<br />
3 Unless a function template specialization has been explicitly instantiated or explicitly specialized, <strong>the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist</strong>. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.</p>
</blockquote>
<p>===&gt;</p>
<p>C++11 schrieb:</p>
<blockquote>
<p>3.2 One definition rule [basic.def.odr]<br />
[...]<br />
3 <strong>Every program shall contain exactly one definition of every non-inline function</strong> or variable <strong>that is odr-used in that program</strong>; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). <strong>An inline function shall be defined in every translation unit in which it is odr-used.</strong></p>
</blockquote>
<p>===&gt;</p>
<p>C++11 schrieb:</p>
<blockquote>
<p>3.2 One definition rule [basic.def.odr]<br />
[...]<br />
2 <strong>An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof</strong>. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. this is odr-used if it appears as a potentially-evaluated expression (including as the result of the implicit transformation in the body of a non-static member function (9.3.1)). A virtual member function is odr-used if it is not pure. <strong>A non-overloaded function whose name appears as a potentially-evaluated expression or a member of a set of candidate functions, if selected by overload resolution when referred to from a potentially-evaluated expression, is odr-used</strong>, unless it is a pure virtual function and its name is not explicitly qualified. [ Note: This covers calls to named functions (5.2.2), operator overloading (Clause 13), user-defined conversions (12.3.2), allocation function for placement new (5.3.4), as well as non-default initialization (8.5). A copy constructor or move constructor is odr-used even if the call is actually elided by the implementation. —end note ][...]</p>
</blockquote>
<p>Der Ausdruck</p>
<pre><code class="language-cpp">myFunc(makeBar(t));
</code></pre>
<p>wird potentiell ausgewerted in obigem Sinne (unabhängig davon, dass man ggf. beweisen könnte, dass er für einen bestimmten Typ nie tatsächlich ausgewertet wird), folglich muss die entsprechende Funktion existieren, folglich wird diese Spezialisierung implizit instantiiert werden.</p>
<p>Das ganze funktioniert also nur, wenn du es mit konstanten Ausdrücken zu tun hast, denn dann werden Teile, die nicht ausgewertet werden auch nicht odr-genutzt. Beispiel</p>
<pre><code class="language-cpp">template &lt;int N&gt;
struct foo   // Hab den Namen dieser Funktion vergessen 
{
    static const int value = N == 1 ? 1 : N % 2 == 0 ? foo&lt;N/2&gt;::value : foo&lt;3*N+1&gt;::value;
};
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2163442</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163442</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Tue, 03 Jan 2012 21:27:18 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 21:17:55 GMT]]></title><description><![CDATA[<p>Du hast zu viel Zeit, stimmts camper?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163444</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163444</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Tue, 03 Jan 2012 21:17:55 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 21:19:08 GMT]]></title><description><![CDATA[<p>Kann m.E. nur 2. sein, weil Optimierungen zum Laufzeitverhalten gehören und nicht die statische Typprüfung beeinflussen können.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163445</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163445</guid><dc:creator><![CDATA[Bashar]]></dc:creator><pubDate>Tue, 03 Jan 2012 21:19:08 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 21:24:31 GMT]]></title><description><![CDATA[<p>314159265358979 schrieb:</p>
<blockquote>
<p>Du hast zu viel Zeit, stimmts camper?</p>
</blockquote>
<p>Scheint so; schliesslich verschwende ich sie, um deine Frage zu beantworten.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163450</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163450</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Tue, 03 Jan 2012 21:24:31 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 21:33:48 GMT]]></title><description><![CDATA[<p>Wow. Danke für diese tolle Antwort, Camper. Exakt das, was ich gesucht habe. Naja. Dann überlege ich mir wohl besser was neues, um meine Logik zu vereinfachen. In dem Fall könnte ich auch einfach code duplizierung betreiben und beide Fälle explizit behandeln.</p>
<p>//edit foo ist in deinem Flal wohl die Collatz-Folge? <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="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163452</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163452</guid><dc:creator><![CDATA[otze]]></dc:creator><pubDate>Tue, 03 Jan 2012 21:33:48 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 23:24:47 GMT]]></title><description><![CDATA[<p>314159265358979 schrieb:</p>
<blockquote>
<p>Du hast zu viel Zeit, stimmts camper?</p>
</blockquote>
<p>Naja, er ist schon recht selektiv, was die Beantwortung von Fragen angeht. Und wenn's dann soweit ist, gibt's meist 'ne gute, ausführliche…</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163486</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163486</guid><dc:creator><![CDATA[krümelkacker]]></dc:creator><pubDate>Tue, 03 Jan 2012 23:24:47 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Tue, 03 Jan 2012 23:30:06 GMT]]></title><description><![CDATA[<p>Ich hab das jetzt nicht mal böse gemeint, genau das wundert mich eben. Hätte nicht die Motivation wegen jedem Mist den Standard zu durchsuchen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163488</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163488</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Tue, 03 Jan 2012 23:30:06 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Wed, 04 Jan 2012 09:05:38 GMT]]></title><description><![CDATA[<p>otze schrieb:</p>
<blockquote>
<p>//edit foo ist in deinem Flal wohl die Collatz-Folge? <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="😉"
    /></p>
</blockquote>
<p>ja, allerdings ist mir gerade aufgefallen, dass das Beispiel so nicht funktioniert. Auch in diesem Fall kommt es (ohne zusätzliche Spezialisierungen) zu unbegrenzter rekursiver Instantiierung. Damit die Instantiierung abbrechen kann, müsste nachgewiesen werden, dass foo&lt;...&gt;::value in einem konstanten Ausdurck verwendet werden kann. Dass ist aber nur der Fall, wenn es sich um ein konstantes Objekt handelt (dieser Test erfordert keine Instantiierung), der mit einem konstanten Ausdruck initialisiert wurde (hierfür ist eine Instantiierung erforderlich).</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163534</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163534</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 04 Jan 2012 09:05:38 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Wed, 04 Jan 2012 10:10:19 GMT]]></title><description><![CDATA[<p>314159265358979 schrieb:</p>
<blockquote>
<p>Hätte nicht die Motivation wegen jedem Mist den Standard zu durchsuchen.</p>
</blockquote>
<p>Ich wage zu behaupten, dass std::camper da nicht lange suchen muss sondern ziemlich genau weiß, wo die Dinge stehen <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="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163563</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163563</guid><dc:creator><![CDATA[pumuckl]]></dc:creator><pubDate>Wed, 04 Jan 2012 10:10:19 GMT</pubDate></item><item><title><![CDATA[Reply to Sind rekursive Templatefunktionen wohlgeformt? on Wed, 04 Jan 2012 10:27:34 GMT]]></title><description><![CDATA[<p>Das mag schon sein, aber das auch kopieren und so.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2163576</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2163576</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 04 Jan 2012 10:27:34 GMT</pubDate></item></channel></rss>