C
Werner Salomon schrieb:
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?
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!
Das muss so sein. Ich habe mal das ganze mit ein paar #defines modifiziert um die verschiedenen Varianten durchzuspielen
#if OPTYPE < 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&, int )
#endif
template< typename Y > struct X
{
void mach()
{
OP( m_i, m_y );
}
Y m_y;
I m_i;
};
}
namespace n2 {
struct Y {};
}
#if DECLTYPE > 1
namespace n2 {
#endif
DECL( n1::I&, n2::Y )
#if DECLTYPE > 1
}
#endif
int main()
{
using namespace n1;
n2::Y y;
I i;
OP( i, y );
X< n2::Y > x;
x.mach();
return 0;
}
Es müsste sich folgende Matrix ergeben:
OPTYPE 0 1 2
DECLTYPE
0 - - -
1 +/- - -
2 + + +
3 + + +
+ bedeutet kompiliert fehlerfrei, - bedeutet Fehler beim Compilieren.
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>0), Visual C++ lässt das wahrscheinlich passieren.
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>1 immer.
Interessant ist der Fall DECLTYPE=1.
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):
- aus Deklarationen, die beim unqualifizierten bzw. qualifiziertem Lookup im Kontext (d.h. der Menge aller Deklaration bis zu diesem Punkt) der Templatedefinition gefunden werden
- aus Deklarationen, die beim argumentabhängigen Lookup im Kontext der Templatedefinition gefunden werden
- aus Deklarationen, die beim argumentabhängigen Lookup im Kontext der Instantiierung gefunden werden
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>0 geklärt.
Etwas mehrdeutig scheint mir die Formulierung für den Fall DECLTYPE=1, OPTYPE=0 (also Deklaration im globalen Namensraum und Operatorsyntax) zu sein.
C++11 schrieb:
14.6.2 Dependent names [temp.dep]
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:
postfix-expression ( expression-listopt)
where the postfix-expression is an id-expression, the id-expression denotes a dependent name if
— any of the expressions in the expression-list is a pack expansion (14.5.3),
— any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or
— if the unqualified-id of the id-expression is a template-id in which any of the template arguments depends on a template parameter.
If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name.
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.
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 Funktionsaufrufsyntax 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.