Inline namespaces - C++
C++11 introduced inline namespaces, but what are inline namespaces?
Contents
What are inline namespaces?
An inline namespaces in like normal namespaces, but uses the inline
keyword
in its original namespace definition.
Members of an inline namespace are treated as if they are members af the enclosing namespace.
This property is transitive, if a namespace A
, contains an
inline namespace B
, which in turn contains an inline namespace C
,
then the members of namespace C
can be used as though they were members of
A
or B
.
You can compile the following code as an example:
#include <iostream>
namespace A {
inline namespace B {
inline namespace C {
const int foo = 5;
}
} // namespace B
} // namespace A
int main() {
// what you would normally do with normal namespaces
std::cout << A::B::C::foo << std::endl; // OK
// because `namespace C` is `inline`, members of it (`foo` in this case)
// is treated as if it were a member of the enclosing namespace (`B`)
std::cout << A::B::foo << std::endl; // OK
// `namespace B` is `inline` so member `namespace C` is treated as if it were
// a member of `A`
std::cout << A::C::foo << std::endl; // OK
// `namespace B` is `inline` so member `namespace C` is treated as if it were
// a member of `A`, and `namespace C` is also `inline`, so member `foo` is
// treated as a member of `B`, but `B` was also inline so members are treated
// as members of `A`
std::cout << A::foo << std::endl; // OK
return 0;
}
Output:
5
5
5
5
Uses of inline namespace
For versioning
According to Bjarne Stroustrup FAQ about inline namespaces, inline namespaces are to allow versioning for backward-compatibility.
You define multiple inner namespaces, and make the most recent one inline
.
Or anyway, the default one for people who don't care about versioning. 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 my_library.h:
namespace my_library {
#include "V99.h"
#include "V98.h"
}
// file main.cc
#include "my_library.h"
int main() {
my_library::V98::f(1); // old version
my_library::V99::f(1); // new version
my_library::f(1); // default version (the inline namespace)
return 0;
}
For versioning based on the standards
We can also use inline namespace
for versioning our library for multiple
C++ standards using inline namespaces and CPP (C PreProcessor) macros.
One example could be the STL vector
.
If we had inline namespaces from the beginning of C++, in C++98 the header of
<vector>
might have looked like this:
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. 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
.
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 if __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.
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):
// 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 the user apparently knows a more efficient
implementation than the one found in (the user copy of) the 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.
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.
There are other holes by which you could detect the nested namespace, 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).