Do czego służą wewnętrzne przestrzenie nazw?

C++11 pozwala inline namespace s, których wszystkie elementy są również automatycznie w Enklawie namespace. Nie mogę wymyślić żadnego użytecznego zastosowania tego rozwiązania - czy ktoś może podać krótki, zwięzły przykład sytuacji, w której potrzebne jest inline namespace i gdzie jest to najbardziej idiomatyczne rozwiązanie?

(również nie jest dla mnie jasne, co się dzieje, gdy namespace jest zadeklarowana inline w jednej, ale nie wszystkich deklaracjach, które mogą żyć w różnych plikach. Czy to nie błaga o kłopoty?)

Author: Marc Mutz - mmutz, 2012-06-13

2 answers

Przestrzenie nazw w wierszu są funkcją wersjonowania bibliotek zbliżoną do wersjonowania symbolu , ale zaimplementowaną wyłącznie na poziomie C++11 (tj. cross-platform) zamiast być cechą określonego binarnego formatu wykonywalnego(tj. specyficzne dla platformy).

Jest to mechanizm, za pomocą którego autor biblioteki może sprawić, że zagnieżdżona przestrzeń nazw będzie wyglądać i działać tak, jakby wszystkie jej deklaracje znajdowały się w otaczającej przestrzeni nazw. pierwsza Nie-inline namespace i wyglądają i zachowują się tak, jakby ich deklaracje były w którejkolwiek z przestrzeni nazw pomiędzy nimi).

Jako przykład rozważ implementację stl vector. Jeśli mamy wewnętrzne przestrzenie nazw Od początku C++, to w C++98 nagłówek <vector> mógłby 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

W zależności od wartości __cplusplus, wybierana jest jedna lub druga implementacja vector. Jeśli twój kod został napisany w pre-C++98 razy i okaże się, że C++98 wersja vector sprawia Ci problemy podczas aktualizacji kompilatora, "wszystko", co musisz zrobić, to znaleźć odniesienia do std::vector w bazie kodowej i zastąpić je przez std::pre_cxx_1997::vector.

Jest kolejnym standardem, a sprzedawca STL powtarza procedurę ponownie, wprowadzając nową przestrzeń nazw dla std::vector z obsługą emplace_back (która wymaga C++11) i wprowadzając ten jeden iff __cplusplus == 201103L.

OK, więc po co mi do tego nowa funkcja językowa? Mogę już zrobić, co następuje, aby mieć to samo 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

W zależności od wartości __cplusplus, otrzymuję jedną lub drugą implementację.

I miałbyś prawie rację.

Rozważ następujący poprawny kod użytkownika C++98 (dozwolone było już w pełni specjalizować szablony, które żyją w przestrzeni nazw std W 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

Jest to doskonale poprawny kod, w którym użytkownik dostarcza własną implementację wektora dla zbioru typu, w którym najwyraźniej zna bardziej efektywny implementacja niż ta znaleziona w (jej kopii) STL.

ale: gdy tworzysz szablon, musisz to zrobić w przestrzeni nazw, w której został zadeklarowany. Standard mówi, że {[3] } jest zadeklarowany w przestrzeni nazw std, więc to miejsce, w którym użytkownik słusznie spodziewa się specjalizować Typ.

Ten kod działa z nie wersjonowaną przestrzenią nazw std, lub z funkcją inline namespace C++11, ale nie z trickiem wersjonowania, który użył using namespace <nested>, ponieważ ujawnia to szczegóły implementacji, że prawdziwa przestrzeń nazw, w której vector została zdefiniowana, nie była bezpośrednio std.

Istnieją inne otwory, dzięki którym można wykryć zagnieżdżoną przestrzeń nazw (Zobacz komentarze poniżej), ale wewnętrzne przestrzenie nazw podłączają je wszystkie. I to wszystko. Niezmiernie przydatny na przyszłość, ale AFAIK Standard nie przepisuje nazw inline nazw przestrzeni nazw dla własnej biblioteki standardowej( chciałbym jednak udowodnić, że się mylę), więc może być używany tylko dla bibliotek innych firm, a nie sam standard (chyba że producenci kompilatorów uzgodnią schemat nazewnictwa).

 283
Author: Marc Mutz - mmutz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:03

Http://www.stroustrup.com/C++11faq. html # inline-namespace (dokument napisany i utrzymywany przez Bjarne Stroustrupa, który Twoim zdaniem powinien być świadomy większości motywacji dla większości funkcji C++11.)

Zgodnie z tym, ma to umożliwić wersjonowanie dla wstecznej kompatybilności. Definiujesz wiele wewnętrznych przestrzeni nazw i tworzysz ostatnią inline. A w każdym razie, domyślna dla ludzi, którzy nie dbają o wersjonowanie. Przypuszczam, że najnowsza może być przyszłością lub najnowsza wersja, która nie jest jeszcze domyślna.

Podany przykład to:

// 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

Nie od razu rozumiem, dlaczego nie umieszczasz using namespace V99; w przestrzeni nazw Mine, ale nie muszę do końca rozumieć przypadku użycia, aby uwierzyć na słowo Bjarne ' a na temat motywacji Komitetu.

 56
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-28 01:44:01