本文翻译自: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. 注意符号名称中存在rel
和dbg
。
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违反两次编写内部命名空间的名称。
It's actually worse than that; 实际上比这更糟糕; it has to be in the same set of braces. 它必须在同一套括号中。
Unless you try to access the meta-object without fully qualifying it, but the meta-object is hardly ever used directly. 除非您尝试在没有完全限定的情况下访问元对象,否则很难直接使用元对象。