2014-12-11 2 views
10

Ich habe diesen Beispielcodemit Direktive - warum so seltsames Verhalten?

namespace ns1 
{ 
    void foo(int) 
    { 
    } 
} 

namespace ns2 
{ 
    void foo() 
    { 
    } 
    void bar() 
    { 
     using namespace ::ns1; 
     foo(42); // why compiler can't just call ns1::foo? 
    } 
} 

Und es mit dem Fehler nicht kompilieren:

prog.cpp:16:9: error: too many arguments to function ‘void ns2::foo()’ 

fand ich Grund dieses Fehlers in C++ 2003-Standard:

A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [Note: in this context, “contains” means “contains directly or indirectly”. ]

Ist jemand Grundprinzip für diese seltsame Regel? Warum können Namen von Namespace ns1 nicht direkt im Namespace ns2 angezeigt werden?

+0

Da ich noch nie 'using' auf diese Weise verwendet gesehen habe, werde ich eine Vermutung wagen. Es versucht, den Namen in den aktuellen Namespace zu ziehen, und wahrscheinlich nicht so, wie Sie nur in den Funktionskontext ziehen möchten. Die Kollision der beiden Funktionen ist dann wahrscheinlich ein Problem. – Photon

+0

Sie geben dem Compiler eine Auswahl, sowohl ns1 :: foo als auch ns2 :: foo sind im Bereich. Welchen sollte es aussuchen? Der nächste oder der am weitesten entfernte? Gefällt es Ihnen, wenn der Compiler eine globale Variable mit dem gleichen Namen als Funktionsparameter auswählt? Und wenn du "ns1 :: foo natürlich!" Warum schreibst du es nicht einfach so? –

+0

Laut D & E macht using-directives "Namen zugänglich". Da wir (möglicherweise) nicht alle Namen kennen, die zugänglich gemacht werden, ist es konservativer und bringt sie weiter in die Namespace-Hierarchie (damit sie nicht versehentlich Namen verbergen). Der nächstgelegene einschließende Namespace könnte als gemeinsamer Teil des Programms interpretiert werden; alles Umschließende ist weniger spezialisiert, alles Umschlossene ist spezialisierter und sollte daher Vorrang haben. Aber ich rate nur. – dyp

Antwort

9

Ich glaube, das war/ist getan, um Konflikte und Überraschungen zu reduzieren. Namen, die durch eine Direktive using in Geltungsbereich gebracht werden, sind sichtbar, aber alles, was direkt im lokalen Bereich enthalten ist, hat Vorrang vor diesem.

Wenn Sie wirklich ohne eine qualifizierte ID Funktion, die Sie anrufen möchten, machen Sie es mit einer using Erklärung sichtbar statt:

namespace ns1 { 
    void foo(int) { } 
} 

namespace ns2 { 
    void foo() { } 
    void bar() { 
     using ::ns1::foo; 
     foo(42); // No problem; calls ::ns1::foo. 
    } 
} 
+0

Ich kenne die Möglichkeit mit der Verwendung von Deklaration, aber die Frage genau über die Verwendung der Direktive. –

+0

@maxim.yurchuk: Ja - deshalb habe ich zuerst die Frage nach der 'using'-Direktive beantwortet, und erst nachdem ich das getan hatte, fügte ich zusätzliche Informationen über einen möglichen Workaround hinzu. –

+0

über die Verringerung von Konflikten und Überraschungen: Ich denke, dieses Beispiel: https://ideone.com/qdGaCC ist ziemlich überraschend. –

2

ns1::foo wird durch die Erklärung von ns2::foo

Von N3337 versteckt, §3.3.10/1[basic.scope.hiding]

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

Der Abschnitt, den Sie sofort zitiert haben (§7.3.4/2) von

3   A using-directive does not add any members to the declarative region in which it appears. [ Example:

namespace A { 
     int i; 
     namespace B { 
     namespace C { 
      int i; 
     } 
     using namespace A::B::C; 
     void f1() { 
      i = 5; // OK, C::i visible in B and hides A::i 
     } 
     } 
     // more (irrelevant) stuff 
    } 

—end example ]

In Ihrem Fall folgt, wird die using-Direktive die Namen in ns1 den gemeinsamen Vorfahren Einführung Namespace von wo es erscheint, und das von ns1, was den globalen Namespace bedeutet. Es nicht führen sie innerhalb ns2, so dass die Erklärung von ns2::foo verbirgt das von ns1::foo.

Wenn Sie möchten, dass ns1::foo gefunden wird, verwenden Sie stattdessen eine using-Deklaration.

void bar() 
{ 
    using ::ns1::foo; 
    foo(42); 
} 
+0

Ich kenne diese Fakten und verstehe, warum dieser Code nicht kompiliert. Ich fragte nach der Erklärung dieser Regeln: Warum verwenden Deklaration Namen in den Geltungsbereich, aber die Verwendung der Direktive nicht? –

+0

@ maxim.yurchuk Dies ist eine * Erklärung * der Regeln. Wenn Sie nach dem Grund suchen, warum sie so arbeiten, dann ist Jerry Coffins Antwort vielleicht die, nach der Sie suchen. Mir ist kein anderer Grund bekannt. – Praetorian

+0

danke, ich ersetze "Erklärung" zu "Begründung" in Frage. –