Warum "Namespace X verwenden"; ist innerhalb der Klassen- / Strukturebene nicht erlaubt?

86
class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

Bearbeiten : Willst du die Motivation dahinter wissen.

iammilind
quelle
1
@pst: C # hat so etwas nicht using namespace. C # erlaubt etwas Ähnliches, jedoch nur im Dateibereich. Mit C ++ using namespacekönnen Sie einen Namespace in einen anderen integrieren.
Billy ONeal
2
Duplikat dieser Frage ?
Großwolf
@ ZachSaw, ich verstehe deine Besorgnis. Habe versucht, das Qn basierend auf der Relevanz zu schließen. Da dieser Beitrag eine objektivere Antwort und einen Verweis auf den Standard enthält, habe ich ihn offen gehalten. In der Vergangenheit wurden viele meiner älteren Qn durch neuere Qn geschlossen. Manchmal von mir, manchmal von anderen. Bitte kennzeichnen Sie die Diamond Mods, falls Sie der Meinung sind, dass diese Entscheidung nicht angemessen war. Keine harten Gefühle. :-)
iammilind
@iammilind könnte es nicht weniger interessieren TBH. SO ist heutzutage ein Chaos. Das Markieren eines Beitrags, der mit "Ich weiß nicht genau" beginnt, als Antwort enthält jedoch "objektivere Antwort und Verweis auf den Standard". Haha.
Zach Saw
@ZachSaw, ich habe nicht nur über die akzeptierte Antwort gesprochen, sondern über den gesamten Beitrag. Ja, es ist objektiv, aber das Standardzitat ist in dieser Antwort enthalten . Es beginnt mit "Ich weiß nicht", denn selbst im Standard ist es nicht gerechtfertigt, warum "Namespace verwenden" im Inneren nicht erlaubt ist class/struct. Es ist einfach nicht erlaubt. Die akzeptierte Antwort diskutiert jedoch eine sehr logische Begründung, um sie nicht zuzulassen. dh wo zu berücksichtigen Hello::Worldund wo zu berücksichtigen World. Hoffe das klärt den Zweifel.
Iammilind

Antworten:

35

Ich weiß es nicht genau, aber ich vermute, dass das Zulassen im Klassenbereich Verwirrung stiften kann:

namespace Hello
{
    typedef int World;
}

class Blah
{
    using namespace Hello;
public:
    World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
    //Is the using namespace valid in here?
}

Da es keinen offensichtlichen Weg gibt, dies zu tun, sagt der Standard nur, dass Sie nicht können.

Der Grund dafür ist weniger verwirrend, wenn es um Namespace-Bereiche geht:

namespace Hello
{
    typedef int World;
}

namespace Other
{
    using namespace Hello;
    World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
    //We're outside of a namespace; obviously the using namespace doesn't apply here.
    //EDIT: Apparently I was wrong about that... see comments. 
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
    //Ditto
}

namespace Other
{
    //namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
    //Therefore this is unambiguiously right
    World DoSomething()
    {
        //We're inside the namespace, obviously the using namespace does apply here.
    }
}
Billy ONeal
quelle
5
+1, ich habe an diesen Grund gedacht, aber dann gilt dasselbe auch für das using namespace Hello;Innere des Anderen namespace(und das Deklarieren der externFunktion darin).
Iammilind
10
Ich finde es nicht verwirrend. In C ++ geht es nicht um Rätselraten. Wenn es erlaubt wäre, hätte das C ++ ISO-Komitee in der Sprachspezifikation angegeben. Dann würden Sie nicht sagen, dass es verwirrend ist. Andernfalls könnte man sagen, dass selbst dies verwirrend ist: ideone.com/npOeD ... aber dann ist die Regel für eine solche Codierung in der Spezifikation angegeben.
Nawaz
1
@Nawaz: Die meisten Benutzer der Sprache. Ich habe nie gesagt, dass es in C ++ um Vermutungen geht. Ich sage, wenn die Spezifikation entworfen wird, wird sie mit dem Verhalten entworfen, das die meisten Programmierer im Voraus erwarten. Und die Regeln auf dem Papier oft sind verwirrend - die Standard - Versuche eindeutig zu sein , aber es gelingt nicht immer.
Billy ONeal
6
Im ersten Beispiel sollte es sein: Hello::World Blah::DoSomething()oder Blah::World Blah::DoSomething()(wenn es erlaubt war), der Rückgabetyp einer Elementfunktionsdefinition wird nicht als im Umfang der Klasse in der Sprache liegend angesehen, daher muss er qualifiziert werden. Betrachten Sie das gültige Beispiel für das Ersetzen von usingdurch einen typedef Hello::World World;at-Klassenbereich. Es sollte also keine Überraschungen geben.
David Rodríguez - Dribeas
2
Wenn es erlaubt wäre, würde es meiner Meinung nach auf lexikalischer Ebene angewendet. Ich denke, dies ist die "offensichtliche" Lösung, die praktisch keine Überraschungen bietet.
Thomas Eding
17

Weil der C ++ - Standard dies ausdrücklich verbietet. Ab C ++ 03 §7.3.4 [namespace.udir]:

using-Direktive :
    using namespace :: opt  verschachtelter name-spezifizierer opt  namespace-name ;

Eine using-Direktive darf nicht im Klassenbereich erscheinen, sondern kann im Namespace-Bereich oder im Blockbereich erscheinen. [Hinweis: Beim Nachschlagen eines Namespace-Namens in einer using-Direktive werden nur Namespace-Namen berücksichtigt, siehe 3.4.6. ]]

Warum verbietet der C ++ - Standard dies? Ich weiß nicht, fragen Sie ein Mitglied des ISO-Komitees, das den Sprachstandard genehmigt hat.

Adam Rosenfield
quelle
42
Noch eine technisch korrekte, aber nutzlose Antwort; die schlimmste Art. 1) Mehr Menschen als nur das Komitee kennen die Antwort. 2) Ausschussmitglieder nehmen an SO teil 3) Wenn Sie die Antwort nicht kennen (angesichts des Geistes der Frage), warum überhaupt antworten?
Catskul
5
@Catskul: Es ist keine nutzlose Antwort. Es ist sehr nützlich zu wissen, dass der Standard dies ausdrücklich anspricht und verbietet. Es ist auch ironisch, dass die am besten bewertete Antwort mit "Ich weiß nicht genau" beginnt. Außerdem ist der "Standard verbietet es" nicht dasselbe wie "es ist nicht erlaubt, weil der Compiler es nicht erlaubt", da der letztere Fall keine Folgefragen beantworten würde wie: Ist es ein Problem mit meinem Compiler? Ist der Compiler nicht standardkonform? Ist es eine Nebenwirkung einiger anderer Dinge, die mir nicht bewusst sind? usw.
Antonone
9

Ich glaube, das Grundprinzip ist, dass es wahrscheinlich verwirrend wäre. Derzeit wird bei der Verarbeitung einer Kennung auf Klassenebene die Suche zuerst im Klassenbereich und dann im umschließenden Namespace gesucht. Das Zulassen using namespaceauf Klassenebene hätte einige Nebenwirkungen auf die Art und Weise, wie die Suche jetzt durchgeführt wird. Insbesondere müsste es irgendwann zwischen der Überprüfung dieses bestimmten Klassenbereichs und der Überprüfung des umschließenden Namespace durchgeführt werden. Das heißt: 1) Zusammenführen der Suchvorgänge auf Klassenebene und verwendeter Namespace-Ebene, 2) Nachschlagen des verwendeten Namespace nach dem Klassenbereich, jedoch vor jedem anderen Klassenbereich, 3) Nachschlagen des verwendeten Namespace direkt vor dem umschließenden Namespace. 4) Suche mit dem umschließenden Namespace zusammengeführt.

  1. Dies würde einen großen Unterschied machen, wo eine Kennung auf Klassenebene würden Schatten jede Kennung in dem umschließenden Namespace, aber es wäre nicht Schatten einen verwendeten Namespace. Der Effekt wäre insofern seltsam, als sich der Zugriff auf den verwendeten Namespace von einer Klasse in einem anderen Namespace und von demselben Namespace unterscheiden würde:

.

namespace A {
   void foo() {}
   struct B {
      struct foo {};
      void f() {
         foo();      // value initialize a A::B::foo object (current behavior)
      }
   };
}
struct C {
   using namespace A;
   struct foo {};
   void f() {
      foo();         // call A::foo
   }
};
  1. Suchen Sie direkt nach diesem Klassenbereich. Dies hätte den seltsamen Effekt, die Mitglieder der Basisklassen zu beschatten. Bei der aktuellen Suche werden keine Suchvorgänge auf Klassen- und Namespace-Ebene gemischt. Wenn Sie eine Klassensuche durchführen, werden die Basisklassen vollständig erfasst, bevor der einschließende Namespace berücksichtigt wird. Das Verhalten wäre insofern überraschend, als es den Namespace nicht auf einer ähnlichen Ebene wie den umschließenden Namespace berücksichtigen würde. Auch hier würde der verwendete Namespace Vorrang vor dem umschließenden Namespace haben.

.

namespace A {
   void foo() {}
}
void bar() {}
struct base {
   void foo();
   void bar();
};
struct test : base {
   using namespace A;
   void f() {
      foo();           // A::foo()
      bar();           // base::bar()
   }
};
  1. Suchen Sie direkt vor dem umschließenden Namespace. Das Problem bei diesem Ansatz ist wiederum, dass es für viele überraschend wäre. Beachten Sie, dass der Namespace in einer anderen Übersetzungseinheit definiert ist, sodass der folgende Code nicht auf einmal angezeigt werden kann:

.

namespace A {
   void foo( int ) { std::cout << "int"; }
}
void foo( double ) { std::cout << "double"; }
struct test {
   using namespace A;
   void f() {
      foo( 5.0 );          // would print "int" if A is checked *before* the
                           // enclosing namespace
   }
};
  1. Mit dem umschließenden Namespace zusammenführen. Dies hätte genau den gleichen Effekt wie das Anwenden der usingDeklaration auf Namespace-Ebene. Dies würde keinen neuen Wert hinzufügen, würde jedoch die Suche nach Compiler-Implementierern erschweren. Die Suche nach Namespace-IDs ist jetzt unabhängig davon, wo im Code die Suche ausgelöst wird. Wenn in einer Klasse die Suche den Bezeichner im Klassenbereich nicht findet, wird auf die Namespace-Suche zurückgegriffen. Dies ist jedoch genau die gleiche Namespace-Suche, die in einer Funktionsdefinition verwendet wird. Es ist nicht erforderlich, einen neuen Status beizubehalten. Wenn die usingErklärung auf Namespace - Ebene gefunden wird, wird der Inhalt des verwendeten sind Namensraum gebracht , in diesem Namensraum für alle Lookups den Namensraum beinhaltet. Wennusing namespace Wurde auf Klassenebene zugelassen, würde es unterschiedliche Ergebnisse für die Namespace-Suche mit genau demselben Namespace geben, je nachdem, von wo aus die Suche ausgelöst wurde, und dies würde die Implementierung der Suche ohne zusätzlichen Wert viel komplexer machen.

Ich empfehle jedenfalls, die Erklärung überhaupt nicht zu verwenden using namespace. Es macht es einfacher, mit Code zu argumentieren, ohne den Inhalt aller Namespaces berücksichtigen zu müssen.

David Rodríguez - Dribeas
quelle
1
Ich bin damit einverstanden, dass die Verwendung dazu neigt, implizite Kuriositäten zu erzeugen. Einige Bibliotheken werden jedoch möglicherweise anhand der vorhandenen Tatsache entworfen using. Indem wir Dinge absichtlich in tief verschachtelten langen Namespaces deklarieren. ZB glmtut dies und verwendet mehrere Tricks, um Funktionen zu aktivieren / präsentieren, wenn der Client sie verwendet using.
v.oddou
sogar direkt in der STL using namespace std::placeholders. cf en.cppreference.com/w/cpp/utility/functional/bind
v.oddou
@ v.oddou:namespace ph = std::placeholders;
David Rodríguez - Dribeas
1

Dies ist wahrscheinlich wegen Offenheit gegen Geschlossenheit nicht erlaubt.

  • Klassen und Strukturen in C ++ sind immer geschlossene Entitäten. Sie werden an genau einer Stelle definiert (obwohl Sie Deklaration und Implementierung aufteilen können).
  • Namespaces können beliebig oft geöffnet, wieder geöffnet und erweitert werden.

Das Importieren von Namespaces in Klassen würde zu lustigen Fällen wie diesen führen:

namespace Foo {}

struct Bar { using namespace Foo; };

namespace Foo {
using Baz = int; // I've just extended `Bar` with a type alias!
void baz(); // I've just extended `Bar` with what looks like a static function!
// etc.
}
DanielS
quelle
0

Ich denke, es ist ein Sprachfehler. Sie können die unten stehende Problemumgehung verwenden. Unter Berücksichtigung dieser Problemumgehung ist es einfach, Regeln für die Lösung von Namenskonflikten für den Fall vorzuschlagen, dass die Sprache geändert wird.

namespace Hello
{
    typedef int World;
}
// surround the class (where we want to use namespace Hello)
// by auxiliary namespace (but don't use anonymous namespaces in h-files)
namespace Blah_namesp {
using namespace Hello;

class Blah
{
public:
    World DoSomething1();
    World DoSomething2();
    World DoSomething3();
};

World Blah::DoSomething1()
{
}

} // namespace Blah_namesp

// "extract" class from auxiliary namespace
using Blah_namesp::Blah;

Hello::World Blah::DoSomething2()
{
}
auto Blah::DoSomething3() -> World
{
}
Naprimeroleg
quelle
Können Sie bitte eine Erklärung hinzufügen?
Kishan Bharda
Ja, ich habe einige Kommentare hinzugefügt
Naprimeroleg