Przestrzenie nazw są funkcją wersjonowania bibliotek, podobną do symbol versioning, ale są implementowane wyłącznie na poziomie C++ 11 (tj. Na wielu platformach), zamiast być cechą określonego formatu binarnego (np. .
Jest to mechanizm, dzięki któremu autor biblioteki może sprawić, że zagnieżdżona przestrzeń nazw wygląda i działa tak, jakby wszystkie jej deklaracje znajdowały się w otaczającym obszarze nazw (wbudowane przestrzenie nazw mogą być zagnieżdżane, więc nazwy "bardziej zagnieżdżone" przenikają całą drogę do pierwszej nieliniowej przestrzeni nazw i wyglądają tak, jakby ich deklaracje znajdowały się w przestrzeni nazw między nimi).
Jako przykład rozważmy implementację STL vector
. Gdybyśmy mieli nazw wbudowanych od początku C++, to w C++ 98 nagłówek <vector>
mógł wyglądać tak:
namespace std {
#if __cplusplus < 1997L // pre-standard C++
inline
#endif
namespace pre_cxx_1997 {
template <class T> __vector_impl; // implementation class
template <class T> // e.g. w/o allocator argument
class vector : __vector_impl<T> { // private inheritance
// ...
};
}
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
# if __cplusplus == 1997L // C++98/03
inline
# endif
namespace cxx_1997 {
// std::vector now has an allocator argument
template <class T, class Alloc=std::allocator<T> >
class vector : pre_cxx_1997::__vector_impl<T> { // the old impl is still good
// ...
};
// and vector<bool> is special:
template <class Alloc=std::allocator<bool> >
class vector<bool> {
// ...
};
};
#endif // C++98/03 or later
} // namespace std
zależności od wartości __cplusplus
, albo jedno albo drugie realizacja vector
jest wybrany. Jeśli twoja baza kodowa została napisana w C++ 98 razy i okazało się, że wersja C++ 98 z vector
powoduje problemy podczas aktualizacji kompilatora, "wszystko", co musisz zrobić, to znaleźć odniesienia do std::vector
w Twojej bazie kodów i zamień je na std::pre_cxx_1997::vector
.
Przyjdź następnego poziomu, a sprzedawca STL tylko powtarza procedurę ponownie, wprowadzając nową nazw dla std::vector
z emplace_back
wsparcia (co wymaga C++ 11) i inline że jeden iff __cplusplus == 201103L
.
OK, dlaczego potrzebuję do tego nowej funkcji językowej? Mogę już wykonać następujące czynności, aby uzyskać ten sam efekt, nie?
namespace std {
namespace pre_cxx_1997 {
// ...
}
#if __cplusplus < 1997L // pre-standard C++
using namespace pre_cxx_1997;
#endif
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
namespace cxx_1997 {
// ...
};
# if __cplusplus == 1997L // C++98/03
using namespace cxx_1997;
# endif
#endif // C++98/03 or later
} // namespace std
zależności od wartości __cplusplus
, mam jeden lub drugi z wdrożeń.
I będziesz prawie poprawny.
Rozważmy następujący ważny C++ 98 kod użytkownika (nie wolno było w pełni specjalizują się szablony, które żyją w przestrzeni nazw std
w C++ 98 już):
// I don't trust my STL vendor to do this optimisation, so force these
// specializations myself:
namespace std {
template <>
class vector<MyType> : my_special_vector<MyType> {
// ...
};
template <>
class vector<MyOtherType> : my_special_vector<MyOtherType> {
// ...
};
// ...etc...
} // namespace std
To jest całkowicie poprawny kod, w którym użytkownik dostarcza własną implementację wektora dla zbioru czcionek, w którym pozornie zna bardziej wydajną implementację niż ta, którą znaleziono (jej kopię) STL.
Ale: Kiedy specjalizujący szablonu, trzeba to zrobić w przestrzeni nazw zostało zadeklarowane w standardowe mówi, że vector
jest zadeklarowana w przestrzeni nazw std
, więc to, gdzie użytkownik słusznie spodziewa się specjalizować typ..
Ten kod działa z non-wersjami nazw std
lub z funkcją przestrzeni nazw C++ 11 inline, ale nie trick wersjami, które kiedyś using namespace <nested>
, ponieważ naraża szczegółów implementacji, że prawdziwa przestrzeń nazw, w której był vector
zdefiniowany bezpośrednio nie był std
.
Istnieją inne dziury, dzięki którym można wykryć zagnieżdżone przestrzenie nazw (zobacz komentarze poniżej), ale wstawione przestrzenie nazw wtyczek je wszystkie. I to wszystko. Niezwykle przydatny w przyszłości, ale AFAIK Standard nie określa wbudowanych nazw przestrzeni nazw dla swojej własnej standardowej biblioteki (chciałbym jednak, aby udowodniono, że to nieprawda), więc można go używać tylko w bibliotekach innych firm, nie sam standard (chyba że producenci kompilatorów zgadzają się na schemat nazewnictwa).
+1 za wyjaśnienie, dlaczego 'using namespace V99;' nie działa w przykładzie Stroustrupa. –
Przypuszczam, że wersjonowanie w standardowych bibliotekach w standardowy sposób jest ograniczone w porównaniu z wersjami innych firm.Jeśli wektor 'C++ 21 'nie jest dla mnie dobry, a ja potrzebuję wektora' C++ 11', to może być to spowodowane zmianą w C++ 21. Ale może to być spowodowane szczegółami implementacji, na których nie powinienem był polegać. Standard nie może wymagać, aby każda implementacja C++ 21 zawierała 'std :: cxx_11 :: vector', która jest zgodna z błędem z jakimkolwiek przeszłym' std :: vector' z tego samego dostawcy. Biblioteki stron trzecich mogą to zrobić, jeśli uważają, że warto. –
I podobnie, jeśli zacznę od zupełnie nowej implementacji C++ 21 od podstaw, to nie chcę obciążać implementacją wielu starych bzdur w 'std :: cxx_11'. Nie każdy kompilator zawsze będzie implementował wszystkie stare wersje standardowych bibliotek, nawet jeśli kusi w tym momencie, że będzie bardzo małym obciążeniem wymagać, aby istniejące implementacje pozostały w starym, gdy dodadzą nowe, ponieważ w rzeczywistości wszystkie i tak są. Przypuszczam, że to, co standard mógł zrobić z pożytkiem, stało się opcjonalne, ale ze standardową nazwą, jeśli jest obecna. –