A member of an inline namespace is treated as if it was a member of the surrounding namespace. Such a member can be partially specialized, explicitly instantiated, or explicitly specialized. This is a transitive property, which means that if a namespace A contains an inline namespace B that contains an inline namespace C, then the members of C appear as they were members of both B and A and the members of B appear as they were members of A.
To better understand why inline namespaces are helpful, let's consider the case of developing a library that evolves over time from a first version to a second version (and further on). This library defines all its types and functions under a namespace called modernlib. In the first version, this library could look like this:
namespace modernlib
{
template<typename T>
int test(T value) { return 1; }
}
A client of the library can make the following call and get back the value 1:
auto x = modernlib::test(42);
However, the client might decide to specialize the template function test() as the following:
struct foo { int a; };
namespace modernlib
{
template<>
int test(foo value) { return value.a; }
}
auto y = modernlib::test(foo{ 42 });
In this case, the value of y is no longer 1, but 42 because the user-specialized function gets called.
Everything is working correctly so far, but as a library developer you decide to create a second version of the library, yet still ship both the first and the second version and let the user control what to use with a macro. In this second version, you provide a new implementation of the test() function that no longer returns 1, but 2. To be able to provide both the first and second implementations, you put them in nested namespaces called version_1 and version_2 and conditionally compile the library using preprocessor macros:
namespace modernlib
{
namespace version_1
{
template<typename T>
int test(T value) { return 1; }
}
#ifndef LIB_VERSION_2
using namespace version_1;
#endif
namespace version_2
{
template<typename T>
int test(T value) { return 2; }
}
#ifdef LIB_VERSION_2
using namespace version_2;
#endif
}
Suddenly, the client code will break, regardless of whether it uses the first or second version of the library. That is because the test function is now inside a nested namespace, and the specialization for foo is done in the modernlib namespace, when it should actually be done in modernlib::version_1 or modernlib::version_2. This is because the specialization of a template is required to be done in the same namespace where the template was declared. In this case, the client needs to change the code like this:
#define LIB_VERSION_2
#include "modernlib.h"
struct foo { int a; };
namespace modernlib
{
namespace version_2
{
template<>
int test(foo value) { return value.a; }
}
}
This is a problem because the library leaks implementation details, and the client needs to be aware of those in order do the template specialization. These internal details are hidden with inline namespaces in the manner shown in the How to do it... section of this recipe. With that definition of the modernlib library, the client code with the specialization of the test() function in the modernlib namespace is no longer broken, because either version_1::test() or version_2::test() (depending on what version the client is actually using) acts as being part of the enclosing modernlib namespace when template specialization is done. The details of the implementation are now hidden to the client that only sees the surrounding namespace modernlib.
However, you should keep in mind that:
- The namespace std is reserved for the standard and should never be inlined.
- A namespace should not be defined inline if it was not inline in its first definition.