C++ inline Namespace: Versioning, API Evolution, and ADL

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 v2 inline; document MyLib::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/v2 binaries.
  • Header-only changes against old binaries can surface as link errors or ODR violations—ship headers and binaries together.
  • Platform inline namespace blocks can isolate platform-specific ABI behind one API name.

Library design checklist

  • One public top-level namespace; prefer inline namespace vN over inline namespace detail (detail is usually non-inline).
  • ADL: verify operators/swap resolve as intended when names lift.
  • Release notes: state that MyLib::Widget is currently v2::Widget.
  • Tests: build against both explicit v1 paths 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).
  • inline variables (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++

  • 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


  • async & launch
  • Atomic operations
  • Attributes
  • auto keyword
  • auto type deduction