<?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[Name lookup und die Praxis]]></title><description><![CDATA[<p>Es geht um folgenden Code, der natürlich nur den Zweck hat, das Problem zu demonstrieren. In der Praxis habe ich ich ein paar Stunden gebraucht, um - beginnend mit der Fehlermeldung des Compilers - zu dieser Beschreibung des Problems zu kommen.</p>
<pre><code class="language-cpp">namespace n1 {
    struct I  {};

    // --   wird folgende Zeile auskommentiert, so kompiliert alles
    void operator+( I&amp; i, int ) {}

    template&lt; typename Y &gt; struct X 
    {
        void mach()
        {
            m_i + m_y; // &lt;--- Fehler, wenn void operator+( I&amp; i, int ) {} vorhanden ist
        }
        Y m_y;
        I m_i;
    };
}

namespace n2 {
    struct Y {};
}

//namespace n2 { // wird die Funktion in den namespace n2 (oder n1) verlegt, geht alles!
    void operator+( n1::I&amp; /*i*/, n2::Y )
    {}
//}

int main()
{
    using namespace n1;
    n2::Y y;
    I i;
    i + y; // &lt;--- compiliert in jedem Fall

    X&lt; n2::Y &gt; x;
    x.mach();
    return 0;
}
</code></pre>
<p>Wenn man diesen Code mit VC8 oder V10 oder dem aktuellen online-Comeau C/C++ übersetzt, so meldet der Compiler in Zeile 11 den Fehler, dass es keinen operator+ gibt ( <code>binary '+' : no operator found which takes a right-hand operand of type 'n2::Y'</code> ). Und das obwohl der identische Operator in Zeile 32 ohne Probleme übersetzt wurde.<br />
Kommentiert man in Zeile 5 den operator+ aus, so kompiliert und läuft alles ohne Probleme.</p>
<p>Des Rätsels Lösung liegt im name lookup des Compilers. Der findet in dem aktuellen namespace eine Funktion gleichen Namens ( <code>operator+</code> ) und mit gleicher Anzahl von Parametern (2), die aber nicht passt, da ein Objekt vom Typ <code>n2::Y</code> eben nicht in ein int zu konvertieren ist. Und dann sagt er 'Fehler' und - und das ist das Problem - und sucht nicht weiter; zumindest nicht in einem anderen namespaces (also hier im globalen).</p>
<p>Nun die Frage: muss das so sein? Ist dieses Verhalten im Standard so beschrieben (wo - Kapitel 3.4)? und was für einen Sinn macht das?<br />
Es wäre mir ja noch verständlich, wenn der Compiler auch dann einen Fehler liefert, wenn die Zeile 5 nicht vorhanden ist. Dann ist es zumindest eindeutig. Aber das ist ja nicht der Fall!</p>
<p>Ich habe später noch diese Antwort von camper <a href="http://www.c-plusplus.net/forum/p1274229#1274229" rel="nofollow">http://www.c-plusplus.net/forum/p1274229#1274229</a> gefunden. Vielleicht kann man das hier noch mal diskutieren.</p>
<p>:xmas2: Werner</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/296884/name-lookup-und-die-praxis</link><generator>RSS for Node</generator><lastBuildDate>Fri, 14 Aug 2026 17:07:56 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/296884.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 14 Dec 2011 14:22:18 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Name lookup und die Praxis on Wed, 14 Dec 2011 14:22:18 GMT]]></title><description><![CDATA[<p>Es geht um folgenden Code, der natürlich nur den Zweck hat, das Problem zu demonstrieren. In der Praxis habe ich ich ein paar Stunden gebraucht, um - beginnend mit der Fehlermeldung des Compilers - zu dieser Beschreibung des Problems zu kommen.</p>
<pre><code class="language-cpp">namespace n1 {
    struct I  {};

    // --   wird folgende Zeile auskommentiert, so kompiliert alles
    void operator+( I&amp; i, int ) {}

    template&lt; typename Y &gt; struct X 
    {
        void mach()
        {
            m_i + m_y; // &lt;--- Fehler, wenn void operator+( I&amp; i, int ) {} vorhanden ist
        }
        Y m_y;
        I m_i;
    };
}

namespace n2 {
    struct Y {};
}

//namespace n2 { // wird die Funktion in den namespace n2 (oder n1) verlegt, geht alles!
    void operator+( n1::I&amp; /*i*/, n2::Y )
    {}
//}

int main()
{
    using namespace n1;
    n2::Y y;
    I i;
    i + y; // &lt;--- compiliert in jedem Fall

    X&lt; n2::Y &gt; x;
    x.mach();
    return 0;
}
</code></pre>
<p>Wenn man diesen Code mit VC8 oder V10 oder dem aktuellen online-Comeau C/C++ übersetzt, so meldet der Compiler in Zeile 11 den Fehler, dass es keinen operator+ gibt ( <code>binary '+' : no operator found which takes a right-hand operand of type 'n2::Y'</code> ). Und das obwohl der identische Operator in Zeile 32 ohne Probleme übersetzt wurde.<br />
Kommentiert man in Zeile 5 den operator+ aus, so kompiliert und läuft alles ohne Probleme.</p>
<p>Des Rätsels Lösung liegt im name lookup des Compilers. Der findet in dem aktuellen namespace eine Funktion gleichen Namens ( <code>operator+</code> ) und mit gleicher Anzahl von Parametern (2), die aber nicht passt, da ein Objekt vom Typ <code>n2::Y</code> eben nicht in ein int zu konvertieren ist. Und dann sagt er 'Fehler' und - und das ist das Problem - und sucht nicht weiter; zumindest nicht in einem anderen namespaces (also hier im globalen).</p>
<p>Nun die Frage: muss das so sein? Ist dieses Verhalten im Standard so beschrieben (wo - Kapitel 3.4)? und was für einen Sinn macht das?<br />
Es wäre mir ja noch verständlich, wenn der Compiler auch dann einen Fehler liefert, wenn die Zeile 5 nicht vorhanden ist. Dann ist es zumindest eindeutig. Aber das ist ja nicht der Fall!</p>
<p>Ich habe später noch diese Antwort von camper <a href="http://www.c-plusplus.net/forum/p1274229#1274229" rel="nofollow">http://www.c-plusplus.net/forum/p1274229#1274229</a> gefunden. Vielleicht kann man das hier noch mal diskutieren.</p>
<p>:xmas2: Werner</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156865</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156865</guid><dc:creator><![CDATA[Werner Salomon]]></dc:creator><pubDate>Wed, 14 Dec 2011 14:22:18 GMT</pubDate></item><item><title><![CDATA[Reply to Name lookup und die Praxis on Wed, 14 Dec 2011 14:41:57 GMT]]></title><description><![CDATA[<p>Der Fehler in Zeile 11 tritt erst auf, wenn diese Funktion wegen Zeile 35 instantiiert wird, richtig?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156874</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156874</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 14 Dec 2011 14:41:57 GMT</pubDate></item><item><title><![CDATA[Reply to Name lookup und die Praxis on Wed, 14 Dec 2011 14:46:02 GMT]]></title><description><![CDATA[<p>camper schrieb:</p>
<blockquote>
<p>Der Fehler in Zeile 11 tritt erst auf, wenn diese Funktion wegen Zeile 35 instantiiert wird, richtig?</p>
</blockquote>
<p>korrekt .. noch ein Punkt mehr, der es in der Praxis so gemein macht. Man hockt davor, und weiß nicht mehr, ob der Compiler spinnt oder man selbst.<br />
Bei mir waren zwei fremde Bibliotheken und eine eigene beteiligt, um dieses Problem zu generieren. Da findet man das nicht so einfach.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156876</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156876</guid><dc:creator><![CDATA[Werner Salomon]]></dc:creator><pubDate>Wed, 14 Dec 2011 14:46:02 GMT</pubDate></item><item><title><![CDATA[Reply to Name lookup und die Praxis on Wed, 14 Dec 2011 14:50:03 GMT]]></title><description><![CDATA[<p>Das alles hat mit Templates erst mal überhaupt nichts zu tun. Du läufst in folgende Problematik:</p>
<pre><code class="language-cpp">void foo(int) { }

namespace ns {
  void foo() { }
  void bar() { foo(1); } // kompiliert auch nicht.
}

int main() {
  ns::bar();
}
</code></pre>
<p>Dass das funktioniert, wenn der operator+ in namespace ns2 deklariert wird, liegt an ADNL (vgl. 3.4.2 im Standard). Beschrieben ist das im Standard in 3.4.1, konkret (1):</p>
<p>ISO/IEC 14882:2003 4.3.1 (1) schrieb:</p>
<blockquote>
<p>In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; <strong>name lookup ends as soon as a declaration is found for the name.</strong> If no declaration is found, the program is ill-formed.</p>
</blockquote>
<p>Ob man sich dabei konkret etwas gedacht hat oder nur bestehende Praxis festgeschrieben wurde, kann ich nicht sagen, weil ich dem Standardkomitee nicht angehöre. Allerdings ergäben sich andernfalls mitunter verwirrende Sachverhalte. Beispiel:</p>
<pre><code class="language-cpp">void foo(double) { }

namespace ns {
  void foo(int) { }
  bar() { foo(1.2); } // Welches foo wird jetzt aufgerufen?
}
</code></pre>
<p>Wahrscheinlich kann man sich darüber streiten, ob das so oder so besser wäre. Probleme hat mir das aber bisher nie gemacht.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156880</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156880</guid><dc:creator><![CDATA[seldon]]></dc:creator><pubDate>Wed, 14 Dec 2011 14:50:03 GMT</pubDate></item><item><title><![CDATA[Reply to Name lookup und die Praxis on Wed, 14 Dec 2011 18:03:41 GMT]]></title><description><![CDATA[<p>Werner Salomon schrieb:</p>
<blockquote>
<p>Nun die Frage: muss das so sein? Ist dieses Verhalten im Standard so beschrieben (wo - Kapitel 3.4)? und was für einen Sinn macht das?<br />
Es wäre mir ja noch verständlich, wenn der Compiler auch dann einen Fehler liefert, wenn die Zeile 5 nicht vorhanden ist. Dann ist es zumindest eindeutig. Aber das ist ja nicht der Fall!</p>
</blockquote>
<p>Das muss so sein. Ich habe mal das ganze mit ein paar #defines modifiziert um die verschiedenen Varianten durchzuspielen</p>
<pre><code class="language-cpp">#if OPTYPE &lt; 2
#  define DECL( x,y ) void operator+( x, y ) {}
#else
#  define DECL( x,y ) void foo( x, y ) {}
#endif

#if OPTYPE == 0
#  define OP( x, y ) x + y
#elif OPTYPE == 1
#  define OP( x, y ) operator+( x, y )
#elif OPTYPE == 2
#  define OP( x, y ) foo( x, y )
#endif

namespace n1 {
    struct I  {};
#if DECLTYPE % 2 == 0
    DECL( I&amp;, int )
#endif

    template&lt; typename Y &gt; struct X
    {
        void mach()
        {
            OP( m_i, m_y );
        }
        Y m_y;
        I m_i;
    };
}

namespace n2 {
    struct Y {};
}

#if DECLTYPE &gt; 1
namespace n2 {
#endif
    DECL( n1::I&amp;, n2::Y )
#if DECLTYPE &gt; 1
}
#endif

int main()
{
    using namespace n1;
    n2::Y y;
    I i;
    OP( i, y );

    X&lt; n2::Y &gt; x;
    x.mach();
    return 0;
}
</code></pre>
<p>Es müsste sich folgende Matrix ergeben:</p>
<pre><code>OPTYPE      0      1      2
DECLTYPE
0                   -      -      -
1                   +/-    -      -
2                   +      +      +
3                   +      +      +
</code></pre>
<p>+ bedeutet kompiliert fehlerfrei, - bedeutet Fehler beim Compilieren.<br />
Compiler mit korrekt implementiertem 2-Phasen-Lookup erkennt man dabei daran, dass sie DECLTYPE==1 für zurückweisen, das ist beispielsweise fürr gcc ab Version 4.7 der Fall (jedenfalls für OPTYPE&gt;0), Visual C++ lässt das wahrscheinlich passieren.</p>
<p>Der globale Namensraum wird während der argumentabhängigen Namenssuche nicht berücksichtigt, schlicht weil keins der Argumente mit diesem assoziert ist (fundamentale Typen haben keine assozierten Namensräume, nicht mal den globalen Namensraum). Eine Deklaration dort kann also nur durch unqualifiziertes Lookup gefunden werden, und das setzt voraus, dass diese Deklaration nicht durch eine Deklaration im Namensraum n1 verdeckt wird. Folglich schlägt DECLTYPE==0 immer fehl. Der Namensraum n2 ist dagegen immer assozierter Namensraum in den Ausdrücken, die hier untersucht werden, und die entsprechende Deklaration wird dann gefunden, folglich passieren DECLTYPE&gt;1 immer.</p>
<p>Interessant ist der Fall DECLTYPE=1.<br />
In der Funktion mach ist + bzw. foo ein abhängiger Name, weil einer der Operatoren vom Typ des Templateparameters abhängt (14.6.2.1). Solche abhängigen Namen werden erst bei der Instantiierung gebunden und werden dabei grundsätzliche folgende Namen gefunden (14.6.4 und 14.6.4.2):<br />
- aus Deklarationen, die beim unqualifizierten bzw. qualifiziertem Lookup im Kontext (d.h. der Menge aller Deklaration bis zu diesem Punkt) der Templatedefinition gefunden werden<br />
- aus Deklarationen, die beim argumentabhängigen Lookup im Kontext der Templatedefinition gefunden werden<br />
- aus Deklarationen, die beim argumentabhängigen Lookup im Kontext der Instantiierung gefunden werden</p>
<p>Sofern keine Deklaration in n1 stört, wird also beim unqualifizierten Lookup der globale Namensraum untersucht, allerdings ist dieser - im Kontext der Templatedefinition - noch leer. Damit ist der Fall DECLTYPE==1, OPTYPE&gt;0 geklärt.<br />
Etwas mehrdeutig scheint mir die Formulierung für den Fall DECLTYPE=1, OPTYPE=0 (also Deklaration im globalen Namensraum und Operatorsyntax) zu sein.</p>
<p>C++11 schrieb:</p>
<blockquote>
<p>14.6.2 Dependent names [temp.dep]<br />
1 Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters. In particular, types and expressions may depend on the type and/or value of template parameters (as determined by the template arguments) and this determines the context for name lookup for certain names. Expressions may be type-dependent (on the type of a template parameter) or value-dependent (on the value of a non-type template parameter). In an expression of the form:<br />
postfix-expression ( expression-listopt)<br />
where the postfix-expression is an id-expression, the id-expression denotes a dependent name if<br />
— any of the expressions in the expression-list is a pack expansion (14.5.3),<br />
— any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or<br />
— if the unqualified-id of the id-expression is a template-id in which any of the template arguments depends on a template parameter.<br />
<strong>If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name.<br />
Such names are unbound and are looked up at the point of the template instantiation (14.6.4.1) in both the context of the template definition and the context of the point of instantiation.</strong></p>
</blockquote>
<p>Etwas unklar bleibt hierbei, ob der zweite Satz nicht nur auf 14.6.4.1 sondern allgemein auf 14.6.4 verweist, oder ob 14.6.4. bzw. 14.6.4.2 eine Spezialisierung für den Fall der Verwendung der Funktionsaufruf<em>syntax</em> sein sollen. Gilt Ersteres, dann wäre der Code zurückzuweisen, andernfalls wäre er in Ordnung (d.h. in diesem Fall wird auch noch ein unqualifiziertes Lookup im Kontext der Instantiierung durchgeführt und findet die entsprechende Deklaration). gcc-4.7 akzeptiert diese Variante, ich weiß aber nicht, wie andere Compiler sich hier verhalten. Persönlich halte ich wenig von dem Gedanken, dass das Lookupergebnis von der verwendeten Syntax abhängen sollte.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156942</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156942</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 14 Dec 2011 18:03:41 GMT</pubDate></item></channel></rss>