C++ Inline Namespace | "인라인 네임스페이스" 가이드

C++ Inline Namespace | "인라인 네임스페이스" 가이드

이 글의 핵심

C++ Inline Namespace에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.

inline namespace란?

부모 네임스페이스에서 직접 접근 가능한 네임스페이스

namespace MyLib {
    inline namespace v2 {
        void func() {
            std::cout << "v2" << std::endl;
        }
    }
}

// 두 방법 모두 가능
MyLib::func();     // v2 호출
MyLib::v2::func(); // v2 호출

버전 관리

namespace MyLib {
    namespace v1 {
        class Widget {
            int data;
        };
    }
    
    inline namespace v2 {
        class Widget {
            int data;
            std::string name;
        };
    }
}

// 기본적으로 v2 사용
MyLib::Widget w;  // v2::Widget

// 명시적으로 v1 사용
MyLib::v1::Widget w1;  // v1::Widget

실전 예시

예시 1: API 버전 관리

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
}

예시 2: ABI 호환성

namespace MyLib {
    #if MYLIB_VERSION >= 2
        inline namespace v2 {
    #else
        inline namespace v1 {
    #endif
            class Data {
                // 버전별 구현
            };
    #if MYLIB_VERSION >= 2
        }
    #else
        }
    #endif
}

예시 3: 실험적 기능

namespace MyLib {
    inline namespace stable {
        void reliableFunc() {
            std::cout << "안정 버전" << std::endl;
        }
    }
    
    namespace experimental {
        void newFunc() {
            std::cout << "실험 버전" << std::endl;
        }
    }
}

int main() {
    MyLib::reliableFunc();  // 기본
    MyLib::experimental::newFunc();  // 명시적
}

예시 4: 플랫폼별 구현

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();  // 플랫폼별 자동 선택
}

중첩 inline namespace

namespace Outer {
    inline namespace Middle {
        inline namespace Inner {
            void func() {
                std::cout << "Inner" << std::endl;
            }
        }
    }
}

// 모두 가능
Outer::func();
Outer::Middle::func();
Outer::Middle::Inner::func();

자주 발생하는 문제

문제 1: 이름 충돌

namespace MyLib {
    inline namespace v1 {
        void func() {}
    }
    
    inline namespace v2 {
        void func() {}  // 에러: 모호함
    }
}

문제 2: 버전 전환

// ❌ 여러 inline
namespace MyLib {
    inline namespace v1 {}
    inline namespace v2 {}  // 충돌
}

// ✅ 하나만 inline
namespace MyLib {
    namespace v1 {}
    inline namespace v2 {}
}

문제 3: ADL 영향

namespace MyLib {
    inline namespace v2 {
        struct Data {};
        void func(Data) {}
    }
}

MyLib::Data d;
func(d);  // ADL로 MyLib::v2::func 찾음

문제 4: 문서화

// ✅ 버전 명시
namespace MyLib {
    /// @brief 최신 안정 버전
    inline namespace v2 {
        void func();
    }
    
    /// @brief 레거시 버전
    namespace v1 {
        void func();
    }
}

표준 라이브러리 사용

// std::literals
using namespace std::literals;
auto s = "hello"s;  // std::string

// std::chrono_literals
using namespace std::chrono_literals;
auto duration = 5s;  // 5 seconds

버전 관리 전략 (API 진화)

inline namespace“기본 진입점”을 한 번에 옮기되, 구버전 심볼은 이름으로 고정해 두는 데 쓰입니다.

  • 신규 메이저: v2inline으로 바꾸고, v1MyLib::v1:: 접두로만 쓰이게 문서화합니다.
  • 호환 깨짐이 있는 변경: 타입 레이아웃·함수 시그니처가 바뀌면 새 네임스페이스에 두고, 마이그레이션 가이드를 제공합니다.
  • 점진적 폐기(deprecated): 구버전에 [[deprecated]]를 붙이고, 기본 경로는 새 구현으로 연결합니다.

한 부모 아래 동시에 둘 이상을 inline으로 두면 안 됩니다. 한 시점에 “기본”은 하나만 있어야 합니다.

ABI와의 관계

ABI(Application Binary Interface)는 이름 장식·호출 규약·객체 레이아웃 등이 맞물린 결과입니다.

  • 네임스페이스 이름은 링커가 보는 심볼에 영향을 줄 수 있어, 라이브러리 소비자가 어떤 v1/v2 타입을 링크했는지가 바이너리 호환과 연결됩니다.
  • 헤더만 바꾸고 바이너리는 옛날 같은 상황에서, inline namespace로 기본 심볼이 바뀌면 링크 오류나 ODR 위반이 드러나기도 합니다. 배포 시 헤더·바이너리 세트를 함께 관리해야 합니다.
  • 플랫폼별 inline namespace: Windows vs POSIX 구현을 갈라 넣으면, 같은 API 이름으로 소스 호환을 유지하면서 플랫폼 ABI 차이를 구현 쪽에 가둘 수 있습니다.

실전 라이브러리 설계 체크리스트

  • 공개 최상위 네임스페이스 하나 아래에 inline namespace detail보다는 **inline namespace vN**처럼 의미 있는 버전 태그를 권장합니다(detail은 보통 inline이 아님).
  • ADL: inline namespace 안의 타입은 바깥 이름으로도 보이므로, 연산자·swap 등 ADL 후보가 의도대로 찾아지는지 확인합니다.
  • 문서: “MyLib::Widget은 현재 v2::Widget이다”를 릴리스 노트에 명시합니다.
  • 테스트: v1 고정 경로와 기본 경로 둘 다 빌드 테스트합니다.

C++17 inline 변수와의 구분

  • inline namespace: 이름 공간을 겹쳐 보이게 하는 기능(버전·플랫폼별 별칭).
  • inline 변수(C++17): 여러 번역 단위에 동일 정의를 헤더에 둘 수 있게 하는 ODR 규칙. inline constexpr과 함께 헤더 상수·std::atomic 전역 등에 쓰입니다.

둘 다 “inline”이지만 해결하는 문제가 다릅니다. 네임스페이스 설계와 변수 ODR은 문맥에 맞게 따로 선택합니다.

FAQ

Q1: inline namespace는 언제?

A:

  • API 버전 관리
  • ABI 호환성
  • 실험적 기능

Q2: 여러 inline 가능?

A: 불가. 하나만 inline.

Q3: 성능 영향?

A: 없음. 컴파일 타임 기능.

Q4: 레거시 코드?

A: 명시적 네임스페이스로 접근.

Q5: 표준 라이브러리?

A: literals 네임스페이스가 inline.

Q6: inline namespace 학습 리소스는?

A:

  • “Effective Modern C++”
  • C++11 표준
  • “API Design for C++“

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ namespace | “이름 충돌 방지” 완벽 가이드
  • C++ constexpr 함수 | “컴파일 타임 함수” 가이드
  • C++ User-Defined Literals | “사용자 정의 리터럴” 가이드

관련 글

  • C++ async & launch |
  • C++ Atomic Operations |
  • C++ Attributes |
  • C++ auto 키워드 |
  • C++ auto 타입 추론 | 복잡한 타입을 컴파일러에 맡기기