Внутренние пространства имен - это функция управления версиями библиотек, сродни symbol versioning, но реализована исключительно на уровне C++ 11 (то есть кросс-платформенная) вместо того, чтобы быть функцией определенного бинарного исполняемого формата (то есть для конкретной платформы) ,
Это механизм, с помощью которого автор библиотеки может создавать вложенное пространство имен и действовать так, как если бы все его объявления находились в окружающем пространстве имен (inline namespaces могут быть вложенными, поэтому «более вложенные» имена перколяются вверх к первому непрозрачному пространству имен и смотреть и действовать так, как если бы их объявления также находились в любом из пространств имен между ними).
В качестве примера рассмотрим реализацию STL vector
. Если бы мы имели встроенные пространства имен с начала C++, то в C++ 98 заголовка <vector>
мог выглядеть так:
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
В зависимости от значения __cplusplus
, либо один, либо другой vector
реализации выбрана. Если ваша кодовая база была написана в pre-C++ 98 раз, и вы обнаружите, что версия C++ 98 vector
вызывает у вас проблемы при обновлении вашего компилятора, «все» вам нужно найти, чтобы найти ссылки на std::vector
в вашей кодовой базе и замените их на std::pre_cxx_1997::vector
.
Come следующий стандарт, и поставщик СТЛ просто повторяет процедуру снова, вводя новое пространство имен для std::vector
с поддержкой emplace_back
(что требует C++ 11) и встраивание, что один тогда и только тогда __cplusplus == 201103L
.
ОК, так зачем мне нужна новая языковая функция? Я уже могу сделать следующее, чтобы иметь тот же эффект, нет?
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
В зависимости от значения __cplusplus
, я получаю либо один, либо другой из реализаций.
И вы были бы почти правы.
Рассмотрим следующий действительный C++ 98 кода пользователя (это было разрешено полностью специализировать шаблоны, которые живут в пространстве имен std
в C++ 98 уже):
// 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
Это совершенно правильный код, в котором пользователь поставляет свою собственную реализацию вектора для набора типов, где она, по-видимому, знает более эффективную реализацию, чем та, что была найдена (ее копия) STL.
Но: Когда специализирующийся шаблон, вы должны сделать это в пространстве имен он был объявлен в Стандарт говорит, что vector
объявлен в пространстве имен std
, так что, когда пользователь по праву ожидает специализироваться типа..
Этот код работает с не версионируются имен std
, или с функцией пространства имен инлайн на C++ 11, но не с версиями трюк, который использовал using namespace <nested>
, потому что подвергает детали реализации, что истинное пространство имен, в котором vector
было не было std
напрямую.
Есть и другие отверстия, с помощью которых вы можете обнаружить вложенное пространство имен (см. Комментарии ниже), но встроенные пространства имен подключают их все. И это все, что нужно. Очень полезно в будущем, но AFAIK Standard не предписывает имена встроенных имен для собственной стандартной библиотеки (я бы хотел, чтобы это было доказано неправильно), поэтому его можно использовать только для сторонних библиотек, а не для сам стандарт (если поставщики компилятора не согласны с схемой именования).
+1 для объяснения, почему 'using namespace V99;' не работает в примере Stroustrup. –
Я полагаю, что управление версиями в стандартных библиотеках стандартным образом ограничено по сравнению с версией сторонних разработчиков.Если C++ 21 'vector' не подходит для меня, и мне нужен C++ 11' vector', то это может быть из-за взломанного изменения в C++ 21. Но это может быть из-за детали реализации, на которую я не должен был полагаться в первую очередь. Стандарт не может требовать, чтобы каждая реализация C++ 21 обеспечивала 'std :: cxx_11 :: vector', который был совместим с любым прошлым' std :: vector' от того же поставщика. Сторонние библиотеки могут это сделать, если они считают, что это того стоит. –
Точно так же, если я начинаю совершенно новую реализацию C++ 21 с нуля, то я не хочу, чтобы меня обременяли реализацией множества старых глупостей в 'std :: cxx_11'. Не каждый компилятор всегда будет реализовывать все старые версии стандартных библиотек, хотя в настоящий момент заманчиво думать, что было бы очень небольшим бременем потребовать, чтобы существующие реализации ушли в старом, когда они добавляют новое, поскольку на самом деле все они в любом случае. Я полагаю, что стандарт мог бы с пользой сделать, сделав его необязательным, но со стандартным именем, если оно присутствует. –