class C {
using namespace std; // error
};
namespace N {
using namespace std; // ok
}
int main () {
using namespace std; // ok
}
Edycja : chcesz poznać motywację za tym.
c++
namespaces
using
language-lawyer
iammilind
źródło
źródło
using namespace
. C # pozwala na coś podobnego, ale tylko w zakresie pliku. C ++using namespace
pozwala na włączenie jednej przestrzeni nazw do innej.class/struct
. To po prostu niedozwolone. Ale przyjęta odpowiedź zawiera bardzo logiczne uzasadnienie, aby tego zabronić. tj. gdzie wziąć pod uwagęHello::World
i gdzie wziąć pod uwagęWorld
. Mam nadzieję, że to rozwiąże wątpliwości.Odpowiedzi:
Nie wiem dokładnie, ale przypuszczam, że zezwolenie na to w zakresie klasy może spowodować zamieszanie:
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? }
Ponieważ nie ma oczywistego sposobu, aby to zrobić, standard mówi tylko, że nie możesz.
Teraz powód, dla którego jest to mniej zagmatwane, gdy mówimy o zakresach przestrzeni nazw:
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. } }
źródło
using namespace Hello;
wewnątrz innychnamespace
(i deklarowaniaextern
funkcji w nim).Hello::World Blah::DoSomething()
lubBlah::World Blah::DoSomething()
(jeśli było to dozwolone), zwracany typ definicji funkcji składowej nie jest uważany za znajdujący się w zakresie klasy w języku, więc musi być kwalifikowany. Rozważmy ważny przykład zastępującusing
ztypedef Hello::World World;
w zakresie klasy. Więc nie powinno być tam żadnych niespodzianek.Ponieważ standard C ++ wyraźnie tego zabrania. Z C ++ 03 §7.3.4 [namespace.udir]:
Dlaczego standard C ++ tego zabrania? Nie wiem, zapytaj członka komitetu ISO, który zatwierdził standard językowy.
źródło
Uważam, że uzasadnienie jest takie, że prawdopodobnie byłoby to zagmatwane. Obecnie, podczas przetwarzania identyfikatora poziomu klasy, wyszukiwanie będzie najpierw przeszukiwać zakres klasy, a następnie otaczającą przestrzeń nazw. Zezwolenie na to
using namespace
na poziomie klasy miałoby pewne skutki uboczne na sposób przeprowadzania wyszukiwania. W szczególności należałoby to wykonać gdzieś pomiędzy sprawdzeniem tego konkretnego zakresu klasy a sprawdzeniem otaczającej przestrzeni nazw. Czyli: 1) scal poziom klasy i zastosowane wyszukiwania na poziomie przestrzeni nazw, 2) wyszukaj używaną przestrzeń nazw po zakresie klasy, ale przed jakimkolwiek innym zakresem klas, 3) wyszukaj używaną przestrzeń nazw tuż przed otaczającą przestrzenią nazw. 4) wyszukiwanie scalone z otaczającą przestrzenią nazw..
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 } };
.
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() } };
.
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 } };
using
deklaracji na poziomie przestrzeni nazw. Nie dodałoby to żadnej nowej wartości, ale z drugiej strony skomplikuje wyszukiwanie implementatorów kompilatora. Wyszukiwanie identyfikatorów przestrzeni nazw jest teraz niezależne od tego, gdzie w kodzie jest uruchamiane wyszukiwanie. Jeśli w klasie funkcja lookup nie znajdzie identyfikatora w zakresie klasy, powróci do wyszukiwania w przestrzeni nazw, ale jest to dokładnie to samo wyszukiwanie przestrzeni nazw, które jest używane w definicji funkcji, nie ma potrzeby utrzymywania nowego stanu. Gdyusing
deklaracja zostanie znaleziona na poziomie przestrzeni nazw, zawartość używanej przestrzeni nazw jest przenoszona do tej przestrzeni nazw dla wszystkich wyszukiwań obejmujących przestrzeń nazw. Jeśliusing namespace
było dozwolone na poziomie klasy, wyniki wyszukiwania przestrzeni nazw dokładnie tej samej przestrzeni nazw byłyby różne w zależności od tego, skąd zostało uruchomione wyszukiwanie, a to sprawiłoby, że implementacja wyszukiwania byłaby znacznie bardziej złożona bez dodatkowej wartości.W każdym razie zalecam, aby w ogóle nie stosować
using namespace
deklaracji. Ułatwia to rozumowanie kodu bez konieczności pamiętania o zawartości wszystkich przestrzeni nazw.źródło
using
. Celowo deklarując rzeczy w głęboko zagnieżdżonych długich przestrzeniach nazw. Np.glm
Robi to i używa wielu sztuczek, aby aktywować / prezentować funkcje, gdy klient używausing
.using namespace std::placeholders
. cf en.cppreference.com/w/cpp/utility/functional/bindnamespace ph = std::placeholders;
Jest to prawdopodobnie niedozwolone ze względu na otwartość kontra zamknięcie.
Importowanie przestrzeni nazw do klas doprowadziłoby do takich śmiesznych przypadków:
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. }
źródło
namespace Foo
do kolejności wyszukiwania dla całego kodu wewnątrz definicji typustruct Bar
, podobnie jak umieszczenie tego wiersza w każdej treści funkcji składowej wbudowanej, z tą różnicą, że byłby również aktywny dla inicjatorów z nawiasami klamrowymi lub równymi itp. wygasają w nawiasie zamykającym, tak samo jakusing namespace
w treści funkcji elementu członkowskiego. Teraz niestety wydaje się, że nie ma sposobu na użycie wyszukiwania Koenig-with-fallback w inicjatorze nawiasów klamrowych lub równych bez zanieczyszczania otaczającej przestrzeni nazw.Myślę, że to wada języka. Możesz zastosować obejście poniżej. Mając na uwadze to obejście, łatwo jest zasugerować zasady rozwiązywania konfliktów nazw w przypadku zmiany języka.
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 { }
źródło