C++ inline Namespace: Versioning, API Evolution, and ADL
이 글의 핵심
Guide to inline namespaces: default API versions, coexistence with older namespaces, and interaction with ADL and ABI.
What is an inline namespace?
Names inside an inline namespace are also found as if they were declared in the enclosing namespace.
namespace MyLib {
inline namespace v2 {
void func() {
std::cout << "v2" << std::endl;
}
}
}
// Both work
MyLib::func(); // v2
MyLib::v2::func(); // v2
Versioning
namespace MyLib {
namespace v1 {
class Widget {
int data;
};
}
inline namespace v2 {
class Widget {
int data;
std::string name;
};
}
}
// Default is v2
MyLib::Widget w; // v2::Widget
// Explicit v1
MyLib::v1::Widget w1;
Practical examples
Example 1: API versions
namespace MyAPI {
namespace v1 {
void process(int x) {
std::cout << "v1: " << x << std::endl;
}
}
inline namespace v2 {
void process(int x, int y = 0) {
std::cout << "v2: " << x << ", " << y << std::endl;
}
}
}
int main() {
MyAPI::process(10); // v2
MyAPI::v1::process(10); // v1
}
Example 2: ABI selection
namespace MyLib {
#if MYLIB_VERSION >= 2
inline namespace v2 {
#else
inline namespace v1 {
#endif
class Data {
// version-specific layout
};
#if MYLIB_VERSION >= 2
}
#else
}
#endif
}
Example 3: Experimental vs stable
namespace MyLib {
inline namespace stable {
void reliableFunc() {
std::cout << "stable" << std::endl;
}
}
namespace experimental {
void newFunc() {
std::cout << "experimental" << std::endl;
}
}
}
int main() {
MyLib::reliableFunc(); // default
MyLib::experimental::newFunc(); // explicit
}
Example 4: Platform split
namespace MyLib {
#ifdef _WIN32
inline namespace windows {
void platformFunc() {
std::cout << "Windows" << std::endl;
}
}
#else
inline namespace posix {
void platformFunc() {
std::cout << "POSIX" << std::endl;
}
}
#endif
}
int main() {
MyLib::platformFunc();
}
Nested inline namespaces
namespace Outer {
inline namespace Middle {
inline namespace Inner {
void func() {
std::cout << "Inner" << std::endl;
}
}
}
}
Outer::func();
Outer::Middle::func();
Outer::Middle::Inner::func();
Common pitfalls
Pitfall 1: Name clashes
namespace MyLib {
inline namespace v1 {
void func() {}
}
inline namespace v2 {
void func() {} // error: ambiguous
}
}
Pitfall 2: Multiple inline siblings
// Bad
namespace MyLib {
inline namespace v1 {}
inline namespace v2 {} // clash
}
// Good: only one inline at a time
namespace MyLib {
namespace v1 {}
inline namespace v2 {}
}
Pitfall 3: ADL
namespace MyLib {
inline namespace v2 {
struct Data {};
void func(Data) {}
}
}
MyLib::Data d;
func(d); // ADL finds MyLib::v2::func
Pitfall 4: Documentation
namespace MyLib {
/// Current stable version
inline namespace v2 {
void func();
}
/// Legacy
namespace v1 {
void func();
}
}
Standard library
using namespace std::literals;
auto s = "hello"s; // std::string
using namespace std::chrono_literals;
auto duration = 5s;
Versioning strategy (API evolution)
inline namespace moves the default entry point while keeping older symbols addressable by explicit qualification.
- New major: make
v2inline; documentMyLib::v1::for legacy. - Breaking layout/signature: put new types in a new namespace and ship a migration guide.
- Deprecation: mark old APIs
[[deprecated]]while default routes to the new implementation.
Only one inline child under a given parent at a time—there must be a single “default.”
ABI notes
ABI couples name mangling, calling conventions, and layout.
- Namespace names affect linker-visible symbols—consumers must link matching
v1/v2binaries. - Header-only changes against old binaries can surface as link errors or ODR violations—ship headers and binaries together.
- Platform
inline namespaceblocks can isolate platform-specific ABI behind one API name.
Library design checklist
- One public top-level namespace; prefer
inline namespace vNoverinline namespace detail(detail is usually non-inline). - ADL: verify operators/swap resolve as intended when names lift.
- Release notes: state that
MyLib::Widgetis currentlyv2::Widget. - Tests: build against both explicit
v1paths and the default path.
C++17 inline variables vs inline namespace
inline namespace: makes a namespace’s names appear in the parent (versioning / platform alias).inlinevariables (C++17): ODR-safe header definitions of data (inline constexpr, etc.).
Same keyword, different problems.
FAQ
Q1: When to use inline namespace?
A:
- API versioning
- ABI compatibility layers
- Experimental vs stable splits
Q2: Multiple inline namespaces?
A: Not as siblings under one parent—only one default.
Q3: Runtime cost?
A: None—purely compile-time name lookup.
Q4: Legacy code?
A: Call explicit v1:: paths.
Q5: Standard library?
A: inline literal namespaces are a familiar example.
Q6: Learning resources?
A:
- Effective Modern C++
- C++11 standard text
- API Design for C++
Related posts (internal links)
- Namespaces complete guide
- constexpr functions
- User-defined literals
Practical tips
Debugging
- Warnings first
Performance
- Profile
Code review
- Conventions
Practical checklist
Before coding
- Right approach?
- Maintainable?
- Performance?
While coding
- Warnings?
- Edge cases?
- Errors?
At review
- Intent?
- Tests?
- Docs?
Keywords
C++, inline namespace, namespace, C++11, versioning
Related posts
- async & launch
- Atomic operations
- Attributes
- auto keyword
- auto type deduction