什麼是內聯命名空間?

本文翻譯自:What are inline namespaces for?

C++11 allows inline namespace s, all members of which are also automatically in the enclosing namespace . C ++ 11允許inline namespace ,其所有成員也自動位於封閉的namespace I cannot think of any useful application of this -- can somebody please give a brief, succinct example of a situation where an inline namespace is needed and where it is the most idiomatic solution? 我想不出任何有用的應用程序 - 有人可以給出一個簡短,簡潔的例子,說明需要inline namespace的情況以及它是最慣用的解決方案嗎?

(Also, it is not clear to me what happens when a namespace is declared inline in one but not all declarations, which may live in different files. Isn't this begging for trouble?) (另外,我不清楚當一個namespace在一個但不是所有聲明中inline聲明時會發生什麼,它可能存在於不同的文件中。這不是乞求麻煩嗎?)


#1樓

參考:https://stackoom.com/question/kDoy/什麼是內聯命名空間


#2樓

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (a document written by and maintained by Bjarne Stroustrup, who you'd think should be aware of most motivations for most C++11 features.) http://www.stroustrup.com/C++11FAQ.html#inline-namespace (由Bjarne Stroustrup編寫並維護的文檔,您認爲應該瞭解大多數C ++ 11功能的大多數動機。 )

According to that, it is to allow versioning for backward-compatibility. 據此,它允許版本化以實現向後兼容性。 You define multiple inner namespaces, and make the most recent one inline . 您可以定義多個內部命名空間,並使最新的內部命名空間成爲inline Or anyway, the default one for people who don't care about versioning. 或者無論如何,對於不關心版本控制的人來說是默認的。 I suppose the most recent one could be a future or cutting-edge version which is not yet default. 我想最新的版本可能是未來或最新版本,但尚未默認。

The example given is: 給出的例子是:

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

I don't immediately see why you don't put using namespace V99; 我沒有立即明白爲什麼你不using namespace V99; inside namespace Mine , but I don't have to entirely understand the use-case in order to take Bjarne's word for it on the committee's motivation. 在命名空間Mine ,但我不必完全理解用例,以便在委員會的動機中採用Bjarne的話。


#3樓

Inline namespaces are a library versioning feature akin to symbol versioning , but implemented purely at the C++11 level (ie. cross-platform) instead of being a feature of a specific binary executable format (ie. platform-specific). 內聯名稱空間是類似於符號版本控制的庫版本控制功能,但純粹在C ++ 11級別(即跨平臺)實現,而不是特定二進制可執行格式(即特定於平臺)的功能。

It is a mechanism by which a library author can make a nested namespace look and act as if all its declarations were in the surrounding namespace (inline namespaces can be nested, so "more-nested" names percolate up all the way to the first non-inline namespace and look and act as if their declarations were in any of the namespaces in between, too). 它是一種機制,通過該機制,庫作者可以使嵌套的命名空間看起來並且就好像它的所有聲明都在周圍的命名空間中一樣(內聯命名空間可以嵌套,因此“更多嵌套”的名稱一直滲透到第一個非命名空間-inline命名空間,看起來和行爲就好像它們的聲明也在它們之間的任何命名空間中)。

As an example, consider the STL implementation of vector . 例如,考慮vector的STL實現。 If we had inline namespaces from the beginning of C++, then in C++98 the header <vector> might have looked like this: 如果我們從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

Depending on the value of __cplusplus , either one or the other vector implementation is chosen. 根據__cplusplus的值, __cplusplus一個或另一個vector實現。 If your codebase was written in pre-C++98 times, and you find that the C++98 version of vector is causing trouble for you when you upgrade your compiler, "all" you have to do is to find the references to std::vector in your codebase and replace them by std::pre_cxx_1997::vector . 如果您的代碼庫是在C ++之前的98次編寫的,並且您發現C ++ 98版本的vector在升級編譯器時會給您帶來麻煩,那麼您需要做的就是查找對你的代碼庫中的std::vector並用std::pre_cxx_1997::vector替換它們。

Come the next standard, and the STL vendor just repeats the procedure again, introducing a new namespace for std::vector with emplace_back support (which requires C++11) and inlining that one iff __cplusplus == 201103L . 下一個標準,STL供應商再次重複該過程,爲std::vector引入一個新的命名空間,支持emplace_back (需要C ++ 11)並內聯一個iff __cplusplus == 201103L

OK, so why do I need a new language feature for this? 好的,爲什麼我需要一個新的語言功能呢? I can already do the following to have the same effect, no? 我已經可以做以下事情來產生同樣的效果,不是嗎?

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

Depending on the value of __cplusplus , I get either one or the other of the implementations. 根據__cplusplus的值,我得到一個或另一個實現。

And you'd be almost correct. 你幾乎是正確的。

Consider the following valid C++98 user code (it was permitted to fully specialize templates that live in namespace std in C++98 already): 考慮以下有效的C ++ 98用戶代碼(允許完全專門化C ++ 98中名稱空間std中的模板):

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

This is perfectly valid code where the user supplies its own implementation of a vector for a set of type where she apparently knows a more efficient implementation than the one found in (her copy of) the STL. 這是完全有效的代碼,其中用戶爲一組類型提供其自己的向量實現,其中她顯然知道比在STL(她的副本)中找到的更有效的實現。

But : When specializing a template, you need to do so in the namespace it was declared in. The Standard says that vector is declared in namespace std , so that's where the user rightfully expects to specialize the type. 但是 :在專門化模板時,您需要在聲明它的名稱空間中執行此操作。標準說, vector是在名稱空間std聲明的,因此用戶正確期望將該類型專門化。

This code works with a non-versioned namespace std , or with the C++11 inline namespace feature, but not with the versioning trick that used using namespace <nested> , because that exposes the implementation detail that the true namespace in which vector was defined was not std directly. 此代碼適用於非版本化命名空間std ,或使用C ++ 11內聯命名空間功能,但不適用於using namespace <nested>的版本控制技巧,因爲它公開了實現細節,即vector所在的真實命名空間定義不是直接std

There are other holes by which you could detect the nested namespace (see comments below), but inline namespaces plug them all. 您可以通過其他漏洞檢測嵌套命名空間(請參閱下面的註釋),但內聯命名空間會將它們全部插入。 And that's all there is to it. 這就是它的全部內容。 Immensely useful for the future, but AFAIK the Standard doesn't prescribe inline namespace names for its own standard library (I'd love to be proven wrong on this, though), so it can only be used for third-party libraries, not the standard itself (unless the compiler vendors agree on a naming scheme). 對於未來非常有用,但AFAIK標準沒有規定其自己的標準庫的內聯命名空間名稱(儘管我喜歡被證明是錯誤的),因此它只能用於第三方庫,而不是標準本身(除非編譯器供應商同意命名方案)。


#4樓

In addition to all the answers above. 除了上面的所有答案。

Inline namespace can be used to encode ABI information or Version of the functions in the symbols. 內聯命名空間可用於編碼符號中的ABI信息或函數版本。 It is due to this reason they are used to provide backward ABI compatibility. 正是由於這個原因,它們被用於提供後向ABI兼容性。 Inline namespaces let you inject information into the mangled name (ABI) without altering the API because they affect linker symbol name only. 內聯命名空間允許您在不更改API的情況下將信息注入到損壞的名稱(ABI)中,因爲它們僅影響鏈接器符號名稱。

Consider this example: 考慮這個例子:

Suppose you write a function Foo that takes a reference to an object say bar and returns nothing. 假設你寫一個函數Foo這需要一個對象的引用,說的bar ,並且沒有返回。

Say in main.cpp 在main.cpp中說

struct bar;
void Foo(bar& ref);

If you check your symbol name for this file after compiling it into an object. 如果在將文件編譯爲對象後檢查此文件的符號名稱。

$ nm main.o
T__ Z1fooRK6bar 

The linker symbol name may vary but it will surely encode the name of function and argument types somewhere. 鏈接器符號名稱可能會有所不同,但它肯定會在某處編碼函數和參數類型的名稱。

Now, it could be that bar is defined as: 現在,它可能是bar被定義爲:

struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};

Depending upon Build type, bar can refer to two different types/layouts with same linker symbols. 根據構建類型, bar可以引用具有相同鏈接器符號的兩種不同類型/佈局。

To prevent such behavior we wrap our struct bar into an inline namespace, where depending upon the Build type the linker symbol of bar will be different. 爲了防止這種行爲,我們將結構bar包裝到內聯命名空間中,根據Build類型, bar的鏈接符號將是不同的。

So, we could write: 所以,我們可以寫:

#ifndef NDEBUG
inline namespace rel { 
#else
inline namespace dbg {
#endif
struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};
}

Now if you look at the object file of each object you build one using release and other with debug flag. 現在,如果您查看每個對象的目標文件,您可以使用release構建一個,另一個使用debug flag。 You will find that linker symbols include inline namespace name as well. 您會發現鏈接器符號也包含內聯命名空間名稱。 In this case 在這種情況下

$ nm rel.o
T__ ZROKfoo9relEbar
$ nm dbg.o
T__ ZROKfoo9dbgEbar

Linker Symbol names may be different. 鏈接器符號名稱可能不同。

Notice presence of rel and dbg in the symbol names. 注意符號名稱中存在reldbg

Now, if you try to link debug with release mode or vise-versa you will get a linker error as contrary to runtime error. 現在,如果您嘗試將調試與發佈模式鏈接或反之亦然,則會出現鏈接器錯誤,這與運行時錯誤相反。


#5樓

I actually discovered another use for inline namespaces. 我實際上發現了內聯命名空間的另一種用法。

With Qt , you get some extra, nice features using Q_ENUM_NS , which in turn requires that the enclosing namespace has a meta-object, which is declared with Q_NAMESPACE . 使用Qt ,您可以使用Q_ENUM_NS獲得一些額外的,不錯的功能,這反過來要求封閉的命名空間有一個元對象,該對象使用Q_NAMESPACE聲明。 However, in order for Q_ENUM_NS to work, there has to be a corresponding Q_NAMESPACE in the same file ⁽¹⁾. 然而,爲了Q_ENUM_NS工作,必須有相應的Q_NAMESPACE 在同一個文件 ⁽¹⁾。 And there can only be one, or you get duplicate definition errors. 並且只能有一個,或者您會得到重複的定義錯誤。 This, effectively, means that all of your enumerations have to be in the same header. 實際上,這意味着所有枚舉必須位於同一標頭中。 Yuck. 呸。

Or... you can use inline namespaces. 或者......您可以使用內聯命名空間。 Hiding enumerations in an inline namespace causes the meta-objects to have different mangled names, while looking to users like the additional namespace doesn't exist⁽²⁾. 隱藏inline namespace枚舉會導致元對象具有不同的受損名稱,同時向用戶查找不存在其他命名空間的情況。

So, they're useful for splitting stuff into multiple sub-namespaces that all look like one namespace, if you need to do that for some reason. 因此,如果由於某種原因需要這樣做,它們對於將東西拆分成多個子命名空間都很有用,這些子命名空間看起來都像一個命名空間。 Of course, this is similar to writing using namespace inner in the outer namespace, but without the DRY violation of writing the name of the inner namespace twice. 當然,這類似於在外部命名空間中using namespace inner編寫,但沒有DRY違反兩次編寫內部命名空間的名稱。


  1. It's actually worse than that; 實際上比這更糟糕; it has to be in the same set of braces. 它必須在同一套括號中。

  2. Unless you try to access the meta-object without fully qualifying it, but the meta-object is hardly ever used directly. 除非您嘗試在沒有完全限定的情況下訪問元對象,否則很難直接使用元對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章