2012-06-13 7 views
260

C++ 11 разрешает inline namespace s, все члены которого также автоматически находятся в прилагаемом namespace. Я не могу придумать какое-либо полезное приложение этого - может ли кто-нибудь, пожалуйста, дать краткий, краткий пример ситуации, когда нужен inline namespace и где это самое идиоматическое решение?Что такое встроенные пространства имен?

(Кроме того, не ясно мне, что происходит, когда namespace объявлен inline в одном, но не все заявления, которые могут жить в разных файлах. Не это ли выпрашивая неприятности?)

ответ

259

Внутренние пространства имен - это функция управления версиями библиотек, сродни 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 не предписывает имена встроенных имен для собственной стандартной библиотеки (я бы хотел, чтобы это было доказано неправильно), поэтому его можно использовать только для сторонних библиотек, а не для сам стандарт (если поставщики компилятора не согласны с схемой именования).

+13

+1 для объяснения, почему 'using namespace V99;' не работает в примере Stroustrup. –

+2

Я полагаю, что управление версиями в стандартных библиотеках стандартным образом ограничено по сравнению с версией сторонних разработчиков.Если C++ 21 'vector' не подходит для меня, и мне нужен C++ 11' vector', то это может быть из-за взломанного изменения в C++ 21. Но это может быть из-за детали реализации, на которую я не должен был полагаться в первую очередь. Стандарт не может требовать, чтобы каждая реализация C++ 21 обеспечивала 'std :: cxx_11 :: vector', который был совместим с любым прошлым' std :: vector' от того же поставщика. Сторонние библиотеки могут это сделать, если они считают, что это того стоит. –

+3

Точно так же, если я начинаю совершенно новую реализацию C++ 21 с нуля, то я не хочу, чтобы меня обременяли реализацией множества старых глупостей в 'std :: cxx_11'. Не каждый компилятор всегда будет реализовывать все старые версии стандартных библиотек, хотя в настоящий момент заманчиво думать, что было бы очень небольшим бременем потребовать, чтобы существующие реализации ушли в старом, когда они добавляют новое, поскольку на самом деле все они в любом случае. Я полагаю, что стандарт мог бы с пользой сделать, сделав его необязательным, но со стандартным именем, если оно присутствует. –

52

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (документ, написанный и поддерживаемый Bjarne Stroustrup, который, по вашему мнению, должен знать о большинстве мотивов для большинства возможностей C++ 11.)

В соответствии с этим, это позволяет управлять версиями для обратной совместимости. Вы определяете несколько внутренних пространств имен и делаете последние inline. Или, во всяком случае, по умолчанию для людей, которые не заботятся об управлении версиями. Я полагаю, что самым последним может быть будущая или передовая версия, которая еще не дефолтна.

Пример, приведенный в:

// file V99.h: 
inline namespace V99 { 
    void f(int); // does something better than the V98 version 
    void f(double); // new feature 
    // ... 
} 

// file V98.h: 
namespace V98 { 
    void f(int); // does something 
    // ... 
} 

// file Mine.h: 
namespace Mine { 
#include "V99.h" 
#include "V98.h" 
} 

#include "Mine.h" 
using namespace Mine; 
// ... 
V98::f(1); // old version 
V99::f(1); // new version 
f(1);  // default version 

Я не сразу понимаю, почему вы не ставите using namespace V99; внутри пространства имен Mine, но я не должен полностью понять потребительной случай для того, чтобы принять Это слово Бьярне для его мотивации.

+0

Значит, последняя версия 'f (1)' будет вызываться из встроенного пространства имен V99? –

+1

@EitanT: да, потому что глобальное пространство имен имеет 'using namespace Mine;', а пространство имен 'Mine' содержит все, от встроенного пространства имен' Mine :: V99'. –

+0

, но для этого мне нужно добавить 'inline' в файл V99.h. Что происходит, когда появляется файл V100.h, объявляя 'inline namespace V100', и я включаю его тоже в' Mine'? Какой 'f (int)' вызывается 'f (1)'? – Walter

Смежные вопросы