C++
Przestrzenie nazw
Szukaj…
Wprowadzenie
Stosowany w celu zapobiegania kolizjom nazw podczas korzystania z wielu bibliotek, przestrzeń nazw jest deklaratywnym przedrostkiem funkcji, klas, typów itp.
Składnia
- identyfikator przestrzeni nazw ( opt ) { deklaracja-seq }
- wbudowany identyfikator przestrzeni nazw ( opt ) { deklaracja-seq } / * od C ++ 11 * /
- inline ( opt ) przestrzeń nazw atrybut-specyfikator- identyfikator -sekw. ( opt ) { deklaracja-sekw. } / * od C ++ 17 * /
- przestrzeń nazw otaczająca-nazwa-specyfikator :: identyfikator { deklaracja-sekw. } / * od C ++ 17 * /
- identyfikator przestrzeni nazw = kwalifikowany-nazwa-specyfikator ;
- using namespace nested-name-specifier ( opt ) namespace-name ;
- atrybut-specifier-seq przy użyciu przestrzeni nazw nested-name-specifier ( opt ) namespace-name ; / * od C ++ 11 * /
Uwagi
Przestrzeń namespace
słów kluczowych ma trzy różne znaczenia w zależności od kontekstu:
Po której następuje opcjonalna nazwa i sekwencja deklaracji w nawiasach klamrowych, definiuje ona nową przestrzeń nazw lub rozszerza istniejącą przestrzeń nazw o te deklaracje. Jeśli nazwa zostanie pominięta, przestrzeń nazw jest przestrzenią nazw bez nazwy .
Po nazwie i znaku równości deklaruje alias przestrzeni nazw .
Kiedy poprzedzone
using
a następnie nazwa przestrzeni nazw, tworzy za pomocą dyrektywy , która pozwala nazw w danej przestrzeni nazw można znaleźć według nazwy niewykwalifikowanego odnośnika (ale nie redeclare te nazwy w bieżącym zakresie). Dyrektywa użycia nie może wystąpić w zakresie klasy.
using namespace std;
jest odradzany. Dlaczego? Ponieważ namespace std
jest ogromna! Oznacza to, że istnieje duże prawdopodobieństwo kolizji nazw:
//Really bad!
using namespace std;
//Calculates p^e and outputs it to std::cout
void pow(double p, double e) { /*...*/ }
//Calls pow
pow(5.0, 2.0); //Error! There is already a pow function in namespace std with the same signature,
//so the call is ambiguous
Jakie są przestrzenie nazw?
Przestrzeń nazw C ++ to zbiór encji C ++ (funkcje, klasy, zmienne), których nazwy są poprzedzone nazwą przestrzeni nazw. Podczas pisania kodu w przestrzeni nazw nazwane podmioty należące do tej przestrzeni nazw nie muszą być poprzedzone nazwą przestrzeni nazw, ale jednostki spoza niej muszą używać w pełni kwalifikowanej nazwy. Pełna nazwa ma format <namespace>::<entity>
nazwa <namespace>::<entity>
. Przykład:
namespace Example
{
const int test = 5;
const int test2 = test + 12; //Works within `Example` namespace
}
const int test3 = test + 3; //Fails; `test` not found outside of namespace.
const int test3 = Example::test + 3; //Works; fully qualified name used.
Przestrzenie nazw są przydatne do grupowania powiązanych definicji. Weź analogię do centrum handlowego. Zasadniczo centrum handlowe jest podzielone na kilka sklepów, z których każdy sprzedaje przedmioty z określonej kategorii. Jeden sklep może sprzedawać elektronikę, podczas gdy inny sklep może sprzedawać buty. Te logiczne separacje w typach sklepów pomagają kupującym znaleźć poszukiwane produkty. Przestrzenie nazw pomagają programistom c ++, takim jak kupujący, znaleźć funkcje, klasy i zmienne, których szukają, organizując je w logiczny sposób. Przykład:
namespace Electronics
{
int TotalStock;
class Headphones
{
// Description of a Headphone (color, brand, model number, etc.)
};
class Television
{
// Description of a Television (color, brand, model number, etc.)
};
}
namespace Shoes
{
int TotalStock;
class Sandal
{
// Description of a Sandal (color, brand, model number, etc.)
};
class Slipper
{
// Description of a Slipper (color, brand, model number, etc.)
};
}
Istnieje predefiniowana pojedyncza przestrzeń nazw, która jest globalną przestrzenią nazw, która nie ma nazwy, ale może być oznaczona przez ::
. Przykład:
void bar() {
// defined in global namespace
}
namespace foo {
void bar() {
// defined in namespace foo
}
void barbar() {
bar(); // calls foo::bar()
::bar(); // calls bar() defined in global namespace
}
}
Tworzenie przestrzeni nazw
Utworzenie przestrzeni nazw jest naprawdę łatwe:
//Creates namespace foo
namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}
Zadzwonić bar
, trzeba najpierw określić przestrzeń nazw, a następnie przez operatora rozdzielczość zakres ::
:
Foo::bar();
Dozwolone jest tworzenie jednej przestrzeni nazw w innej, na przykład:
namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
Powyższy kod można uprościć w następujący sposób:
namespace A::B::C
{
void bar() {}
}
Rozszerzanie przestrzeni nazw
Przydatną funkcją namespace
jest to, że możesz je rozszerzać (dodawać do niego członków).
namespace Foo
{
void bar() {}
}
//some other stuff
namespace Foo
{
void bar2() {}
}
Korzystanie z dyrektywy
Słowo kluczowe „używanie” ma trzy smaki. W połączeniu ze słowem kluczowym „przestrzeń nazw” piszesz „za pomocą dyrektywy”:
Jeśli nie chcesz pisać Foo::
na początku każdego elementu w przestrzeni nazw Foo
, możesz użyć using namespace Foo;
aby zaimportować każdą rzecz z Foo
.
namespace Foo
{
void bar() {}
void baz() {}
}
//Have to use Foo::bar()
Foo::bar();
//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK
Możliwe jest również importowanie wybranych encji w przestrzeni nazw zamiast w całej przestrzeni nazw:
using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported
Uwaga: using namespace
w plikach nagłówkowych jest w większości przypadków postrzegane jako zły styl. Jeśli tak się stanie, przestrzeń nazw zostanie zaimportowana do każdego pliku zawierającego nagłówek. Ponieważ nie ma sposobu na „nie- using
” przestrzeń nazw, może to prowadzić do zanieczyszczenia przestrzeni nazw (lub bardziej nieoczekiwane symbole w globalnej przestrzeni nazw) lub, co gorsza, konfliktów. Zobacz ten przykład, aby zilustrować problem:
/***** foo.h *****/
namespace Foo
{
class C;
}
/***** bar.h *****/
namespace Bar
{
class C;
}
/***** baz.h *****/
#include "foo.h"
using namespace Foo;
/***** main.cpp *****/
#include "bar.h"
#include "baz.h"
using namespace Bar;
C c; // error: Ambiguity between Bar::C and Foo::C
Dyrektywa użycia nie może wystąpić w zakresie klasy.
Wyszukiwanie zależne od argumentu
Podczas wywoływania funkcji bez jawnego kwalifikatora przestrzeni nazw kompilator może wybrać wywołanie funkcji w przestrzeni nazw, jeśli jeden z typów parametrów tej funkcji również znajduje się w tej przestrzeni nazw. Nazywa się to „Argument Dependent Lookup” lub ADL:
namespace Test
{
int call(int i);
class SomeClass {...};
int call_too(const SomeClass &data);
}
call(5); //Fails. Not a qualified function name.
Test::SomeClass data;
call_too(data); //Succeeds
call
kończy się niepowodzeniem, ponieważ żaden z jego typów parametrów nie pochodzi z przestrzeni nazw Test
. call_too
działa, ponieważ SomeClass
jest członkiem Test
i dlatego kwalifikuje się do reguł ADL.
Kiedy ADL nie występuje
ADL nie występuje, jeśli normalne niewykwalifikowane wyszukiwanie znajdzie element klasy, funkcję zadeklarowaną w zakresie bloku lub coś, co nie jest typu funkcji. Na przykład:
void foo();
namespace N {
struct X {};
void foo(X ) { std::cout << '1'; }
void qux(X ) { std::cout << '2'; }
}
struct C {
void foo() {}
void bar() {
foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
}
};
void bar() {
extern void foo(); // redeclares ::foo
foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments
}
int qux;
void baz() {
qux(N::X{}); // error: variable declaration disables ADL for "qux"
}
Wbudowana przestrzeń nazw
inline namespace
zawiera zawartość inline namespace
nazw w otaczającej przestrzeni nazw, więc
namespace Outer
{
inline namespace Inner
{
void foo();
}
}
jest w większości równoważny z
namespace Outer
{
namespace Inner
{
void foo();
}
using Inner::foo;
}
ale elementy z Outer::Inner::
i te powiązane z Outer::
są identyczne.
Więc podążanie jest równoważne
Outer::foo();
Outer::Inner::foo();
Alternatywa using namespace Inner;
nie byłby równoważny dla niektórych trudnych części jako specjalizacja szablonu:
Dla
#include <outer.h> // See below
class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}
Wbudowana przestrzeń nazw pozwala na specjalizację
Outer::foo
// outer.h // include guard omitted for simplification namespace Outer { inline namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } }
Natomiast
using namespace
nie pozwala na specjalizacjęOuter::foo
// outer.h // include guard omitted for simplification namespace Outer { namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } using namespace Inner; // Specialization of `Outer::foo` is not possible // it should be `Outer::Inner::foo`. }
Wbudowana przestrzeń nazw to sposób na umożliwienie współistnienia kilku wersji i domyślnego ustawienia tej inline
namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}
namespace Version1 // The old one
{
void foo();
}
}
I przy użyciu
MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();
Nienazwane / anonimowe przestrzenie nazw
Można użyć nienazwanej przestrzeni nazw, aby upewnić się, że nazwy mają wewnętrzne powiązanie (do której może odwoływać się tylko bieżąca jednostka tłumacząca). Taka przestrzeń nazw jest zdefiniowana w taki sam sposób, jak każda inna przestrzeń nazw, ale bez nazwy:
namespace {
int foo = 42;
}
foo
jest widoczne tylko w jednostce tłumaczenia, w której się pojawia.
Zaleca się, aby nigdy nie używać nienazwanych przestrzeni nazw w plikach nagłówków, ponieważ daje to wersję treści dla każdej jednostki tłumaczeniowej, w której jest zawarty. Jest to szczególnie ważne, jeśli definiujesz globale nie stałe.
// foo.h
namespace {
std::string globalString;
}
// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...
globalString = "Initialize";
// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...
std::cout << globalString; //< Will always print the empty string
Kompaktowe zagnieżdżone przestrzenie nazw
namespace a {
namespace b {
template<class T>
struct qualifies : std::false_type {};
}
}
namespace other {
struct bob {};
}
namespace a::b {
template<>
struct qualifies<::other::bob> : std::true_type {};
}
Można wprowadzić zarówno a
i b
nazw w jednym kroku z namespace a::b
począwszy od C ++ 17.
Aliasing długiej przestrzeni nazw
Jest to zwykle używane do zmiany nazwy lub skrócenia długich odwołań do przestrzeni nazw, takich jak odwołania do składników biblioteki.
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace Name1 = boost::multiprecision;
// Both Type declarations are equivalent
boost::multiprecision::Number X // Writing the full namespace path, longer
Name1::Number Y // using the name alias, shorter
Alias Zakres deklaracji
Na deklarację aliasu ma wpływ wcześniejsze użycie instrukcji
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
using namespace boost;
// Both Namespace are equivalent
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;
Łatwiej jednak pomylić, która przestrzeń nazw jest aliasing, gdy masz coś takiego:
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace numeric
{
namespace multiprecision
{
class Number ...
}
}
using namespace numeric;
using namespace boost;
// Not recommended as
// its not explicitly clear whether Name1 refers to
// numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;
// For clarity, its recommended to use absolute paths
// instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;
Alias przestrzeni nazw
Przestrzeni nazw można nadać alias ( tj. Inną nazwę dla tej samej przestrzeni nazw) za pomocą identyfikatora namespace
=
składnia. Dostęp do członków aliasu przestrzeni nazw można uzyskać, kwalifikując ich przy użyciu nazwy aliasu. W poniższym przykładzie zagnieżdżona przestrzeń nazw AReallyLongName::AnotherReallyLongName
jest niewygodna do AReallyLongName::AnotherReallyLongName
, więc funkcja qux
lokalnie deklaruje alias N
Dostęp do członków tej przestrzeni nazw można uzyskać po prostu za pomocą N::
.
namespace AReallyLongName {
namespace AnotherReallyLongName {
int foo();
int bar();
void baz(int x, int y);
}
}
void qux() {
namespace N = AReallyLongName::AnotherReallyLongName;
N::baz(N::foo(), N::bar());
}