Sök…
Introduktion
Används för att förhindra namnkollisioner vid användning av flera bibliotek, är ett namnområde ett deklarativt prefix för funktioner, klasser, typer etc.
Syntax
- namespace identifierare ( opt ) { deklaration-seq }
- inline namespace identifierare ( opt ) { deklaration-seq } / * sedan C ++ 11 * /
- inline ( opt ) namespace attribut-specifier-seq identifierare ( opt ) { deklaration-seq } / * sedan C ++ 17 * /
- namespace enclosing-namespace-specificier :: identifierare { deklaration-seq } / * sedan C ++ 17 * /
- namespace identifier = kvalificerad namespace-specificer ;
- använda namnutrymme kapselt namnspecifikator ( opt ) namnutrymdsnamn ;
- attributspecifikator-seq med namnutrymme kapselt namn-specifierare ( opt ) namnutrymme-namn ; / * sedan C ++ 11 * /
Anmärkningar
Nyckelordets namespace
har tre olika betydelser beroende på sammanhang:
När det följs av ett valfritt namn och en medföljande sekvens av deklarationer definierar den ett nytt namnutrymme eller utökar ett befintligt namnområde med deklarationerna. Om namnet utelämnas är namnutrymmet ett namngivet namnområde .
När det följs av ett namn och ett lika tecken, förklarar det ett namnutrymme alias .
När det föregås av att
using
och följt av ett namnutrymdsnamn, bildar det ett användningsdirektiv som gör det möjligt att hitta namn i det givna namnområdet genom okvalificerad namnuppsökning (men deklarerar inte dessa namn i det aktuella omfånget). Ett användningsdirektiv kan inte förekomma inom klassens omfattning.
using namespace std;
är avskräckt. Varför? Eftersom namespace std
är enormt! Detta innebär att det finns en stor chans att namn kommer att kollidera:
//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
Vad är namnutrymmen?
Ett C ++ namnutrymme är en samling av C ++ enheter (funktioner, klasser, variabler), vars namn är förinställda med namnet på namnområdet. När du skriver kod i ett namnutrymme behöver namngivna enheter som tillhör det namnutrymmet inte förinställas med namnområdet, men enheter utanför det måste använda det fullt kvalificerade namnet. Det fullt kvalificerade namnet har formatet <namespace>::<entity>
. Exempel:
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.
Namnområden är användbara för att gruppera relaterade definitioner tillsammans. Ta analogin med ett köpcentrum. I allmänhet är ett köpcentrum uppdelat i flera butiker, där varje butik säljer varor från en viss kategori. En butik kan sälja elektronik, medan en annan butik kan sälja skor. Dessa logiska separationer i butikstyper hjälper kunderna att hitta de artiklar de letar efter. Namnytor hjälper c ++ programmerare, som shoppare, att hitta funktionerna, klasserna och variablerna de letar efter genom att organisera dem på ett logiskt sätt. Exempel:
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.)
};
}
Det finns ett fördefinierat enda namnutrymme, som är det globala namnområdet som inte har något namn, men som kan betecknas med ::
. Exempel:
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
}
}
Att skapa namnutrymmen
Att skapa ett namnutrymme är väldigt enkelt:
//Creates namespace foo
namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}
För att ringa bar
måste du ange namnområdet först följt av operatören för omfattningsupplösning ::
Foo::bar();
Det är tillåtet att skapa ett namnutrymme i ett annat, till exempel:
namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
Ovanstående kod kan förenklas till följande:
namespace A::B::C
{
void bar() {}
}
Utöka namnutrymmen
En användbar funktion i namespace
är att du kan utöka dem (lägg till medlemmar i det).
namespace Foo
{
void bar() {}
}
//some other stuff
namespace Foo
{
void bar2() {}
}
Med hjälp av direktiv
Nyckelordet "använder" har tre smaker. I kombination med nyckelordet "namnutrymme" skriver du ett "med direktiv":
Om du inte vill skriva Foo::
framför alla saker i namnområdet Foo
kan du använda using namespace Foo;
att importera allt från Foo
.
namespace Foo
{
void bar() {}
void baz() {}
}
//Have to use Foo::bar()
Foo::bar();
//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK
Det är också möjligt att importera utvalda enheter i ett namnutrymme snarare än i hela namnområdet:
using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported
Ett försiktighetsord: att using namespace
i huvudfiler ses i de flesta fall som dålig stil. Om detta görs importeras namnområdet i varje fil som innehåller rubriken. Eftersom det inte finns något sätt att "oför using
" ett namnutrymme, kan detta leda till namespace föroreningar (mer eller oväntade symboler i den globala namnrymden) eller, ännu värre, konflikter. Se detta exempel för en illustration av problemet:
/***** 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
Ett användningsdirektiv kan inte förekomma inom klassens omfattning.
Argumentberoende uppslag
När man ringer till en funktion utan ett uttryckligt namnutrymme, kan kompilatorn välja att anropa en funktion inom ett namnutrymme om en av parametertyperna till den funktionen också finns i det namnområdet. Detta kallas "Argumentberoende uppslag", eller 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
misslyckas eftersom ingen av dess parametertyper kommer från Test
call_too
fungerar eftersom SomeClass
är medlem i Test
och därför kvalificerar det sig för ADL-regler.
När inträffar inte ADL
ADL inträffar inte om normal okvalificerad sökning hittar en klassmedlem, en funktion som har deklarerats till blockomfång eller något som inte är av funktionstyp. Till exempel:
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"
}
Inline namnutrymme
inline namespace
innehåller innehållet i det inlined namnområdet i det bifogade namnområdet, så
namespace Outer
{
inline namespace Inner
{
void foo();
}
}
motsvarar mestadels
namespace Outer
{
namespace Inner
{
void foo();
}
using Inner::foo;
}
men element från Outer::Inner::
och de som är associerade till Outer::
är identiska.
Så följande är likvärdigt
Outer::foo();
Outer::Inner::foo();
Alternativet using namespace Inner;
skulle inte vara likvärdiga för vissa knepiga delar som mallspecialisering:
För
#include <outer.h> // See below
class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}
Inline-namnområdet ger specialisering av
Outer::foo
// outer.h // include guard omitted for simplification namespace Outer { inline namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } }
Medan det
using namespace
inte tillåter specialisering avOuter::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`. }
Inline namnutrymme är ett sätt att tillåta flera versioner att samverka och som standard inline
namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}
namespace Version1 // The old one
{
void foo();
}
}
Och med användning
MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();
Namnlösa / anonyma namnutrymmen
Ett namngivet namnutrymme kan användas för att säkerställa att namnen har intern länkning (kan endast refereras till av den aktuella översättningsenheten). Ett sådant namnområde definieras på samma sätt som alla andra namnutrymmen, men utan namnet:
namespace {
int foo = 42;
}
foo
är endast synlig i den översättningsenhet där den visas.
Vi rekommenderar att du aldrig använder namngivna namnutrymmen i rubrikfiler eftersom det ger en version av innehållet för varje översättningsenhet som den ingår i. Det är särskilt viktigt om du definierar icke-const globaler.
// 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
Kompakta kapslade namnutrymmen
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 {};
}
Du kan ange både a
och b
namnutrymmen i ett steg med namespace a::b
börjar i C ++ 17.
Aliasing ett långt namnutrymme
Detta används vanligtvis för att döpa om eller förkorta långa namnutrymme-referenser, som hänvisar till komponenter i ett bibliotek.
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
Aliasdeklarationens omfattning
Aliasdeklarationen påverkas av föregående användning av uttalanden
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
using namespace boost;
// Both Namespace are equivalent
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;
Det är dock lättare att bli förvirrad över vilket namnutrymme du alias när du har något liknande:
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;
Namnrymdsalias
Ett namnutrymme kan ges ett alias (det vill säga ett annat namn för samma namnutrymme) med hjälp av namespace
identifierare =
syntax. Du kan få åtkomst till medlemmar i det aliasna namnområdet genom att kvalificera dem med namnet på aliaset. I följande exempel är det kapslade namnutrymmet AReallyLongName::AnotherReallyLongName
obekvämt att skriva, så funktionen qux
förklarar lokalt ett alias N
Medlemmar i det namnutrymmet kan sedan nås helt enkelt med 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());
}