[c++] 인라인 네임 스페이스 란 무엇입니까?

C ++ 11은 inline namespaces를 허용 하며, 모든 멤버도 자동으로 둘러싸고 namespace있습니다. 나는 이것의 유용한 적용을 생각할 수 없다-누군가 inline namespace가 필요한 상황 과 가장 관용적 인 해결책이 있는 상황에 대한 간략하고 간결한 예를 줄 수 있습니까?

(또한 a namespaceinline하나의 파일로 선언되었지만 다른 파일에있을 수있는 모든 선언이 선언되지 않은 경우 어떤 일이 발생하는지 명확 하지 않습니다. 문제가되지 않습니까?)



답변

인라인 네임 스페이스는 심볼 버전 관리 와 유사한 라이브러리 버전 관리 기능 이지만 특정 바이너리 실행 형식 (예 : 플랫폼 별)의 기능이 아닌 C ++ 11 수준 (예 : 크로스 플랫폼)에서 순수하게 구현됩니다.

라이브러리 작성자는 중첩 된 네임 스페이스를 모든 선언이 주변 네임 스페이스에있는 것처럼 보이게하고 작동 할 수있는 메커니즘입니다 (인라인 네임 스페이스는 중첩 될 수 있으므로 “더 중첩 된”이름은 첫 번째가 아닌 이름까지 퍼집니다) -인라인 네임 스페이스를 사용하고 선언이 그 사이의 네임 스페이스에있는 것처럼 보입니다.

예를 들어의 STL 구현을 고려하십시오 vector. C ++의 시작부터 인라인 네임 스페이스가 있다면 C ++ 98에서 헤더 <vector>는 다음과 같을 것입니다.

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

의 값에 따라 __cplusplus하나 또는 다른 vector구현이 선택됩니다. 코드베이스가 C ++ 98 이전 버전으로 작성되었고 vector컴파일러를 업그레이드 할 때 C ++ 98 버전으로 인해 문제가 발생하는 경우 “모두”는 참조를 찾는 것 std::vector입니다. 코드베이스와로 대체하십시오 std::pre_cxx_1997::vector.

을위한 새로운 공간 도입, 다음 기본으로 제공하고, STL 공급 업체는 절차를 다시 반복 std::vectoremplace_back(++ 11 C 필요)를 지원하고 하나 IFF를 인라인 __cplusplus == 201103L.

그렇다면 왜 새로운 언어 기능이 필요합니까? 나는 이미 같은 효과를 내기 위해 다음을 할 수 있습니까?

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

의 가치에 따라 __cplusplus구현 중 하나를 얻습니다.

그리고 당신은 거의 정확할 것입니다.

다음과 같은 유효한 C ++ 98 사용자 코드를 고려하십시오 ( std이미 C ++ 98의 네임 스페이스 에 있는 템플릿을 완전히 특수화 할 수 있음).

// 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

이것은 사용자가 STL에서 찾은 것보다 더 효율적인 구현을 알 수있는 유형 집합에 대해 자체 벡터 구현을 제공하는 완벽하게 유효한 코드입니다.

그러나 : 템플릿을 특수화 할 때는 vector선언 된 네임 스페이스 std에서 그렇게해야 합니다. 표준 에서는 네임 스페이스에 선언되어 있으므로 사용자가 형식을 특수하게 지정해야합니다.

이 코드는 버전이없는 네임 스페이스 std또는 C ++ 11 인라인 네임 스페이스 기능과 함께 작동하지만 사용 된 버전 관리 트릭과 는 작동하지 않습니다 . using namespace <nested>이는 vector정의 된 실제 네임 스페이스가 std직접적 으로 구현되지 않았 음을 구현 세부 사항에 노출시키기 때문입니다 .

중첩 된 네임 스페이스를 감지 할 수있는 다른 구멍이 있지만 (아래 주석 참조) 인라인 네임 스페이스는 모두 연결합니다. 그리고 그것이 전부입니다. 미래에는 엄청나게 유용하지만 표준 AFAIK는 자체 표준 라이브러리에 대해 인라인 네임 스페이스 이름을 지정하지 않으므로 (이 경우 잘못된 것으로 입증되고 싶습니다) 타사 라이브러리에만 사용할 수 있습니다. 표준 자체 (컴파일러 벤더가 이름 지정 체계에 동의하지 않는 한).


답변

http://www.stroustrup.com/C++11FAQ.html#inline-namespace(Bjarne Stroustrup이 작성하고 유지 관리하는 문서로 대부분의 C ++ 11 기능에 대한 대부분의 동기를 알고 있어야한다고 생각합니다. )

이에 따르면, 이전 버전과의 호환성을 위해 버전 관리를 허용하는 것입니다. 여러 개의 내부 네임 스페이스를 정의하고 가장 최근 네임 스페이스를 만듭니다 inline. 어쨌든, 버전 관리에 관심이없는 사람들을위한 기본 설정입니다. 가장 최신 버전이 아직 기본이 아닌 미래 또는 최신 버전 일 수 있다고 생각합니다.

주어진 예는 다음과 같습니다.

// 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 Mine.h:
namespace Mine {
#include "V99.h"
#include "V98.h"
}

#include "Mine.h"
using namespace Mine;
// ...
V98::f(1);  // old version
V99::f(1);  // new version
f(1);       // default version

나는 왜 당신이 using namespace V99;namespace에 넣지 않았는지 즉시 알지 Mine못하지만,위원회의 동기 부여에 대한 Bjarne의 말을 취하기 위해 유스 케이스를 완전히 이해할 필요는 없습니다.


답변

다른 모든 답변 외에도.

인라인 네임 스페이스를 사용하여 ABI 정보 또는 기호의 함수 버전을 인코딩 할 수 있습니다. 이러한 이유로 인해 이전 ABI 호환성을 제공하는 데 사용됩니다. 인라인 네임 스페이스를 사용하면 링커 심볼 이름에만 영향을 미치므로 API를 변경하지 않고도 정보를 맹 글링 된 이름 (ABI)에 삽입 할 수 있습니다.

이 예제를 고려하십시오.

Foo객체에 대한 참조를 가져 와서 bar아무것도 반환하지 않는 함수를 작성한다고 가정하십시오 .

main.cpp에서 말해

struct bar;
void Foo(bar& ref);

이 파일을 객체로 컴파일 한 후이 파일의 심볼 이름을 확인하면

$ nm main.o
T__ Z1fooRK6bar 

링커 심볼 이름은 다를 수 있지만 반드시 함수 이름과 인수 유형을 어딘가에 인코딩해야합니다.

이제 bar다음과 같이 정의 될 수 있습니다 .

struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};

빌드 유형에 따라 bar동일한 링커 기호가있는 두 가지 유형 / 레이아웃을 참조 할 수 있습니다.

이러한 동작을 방지하기 위해 bar빌드 유형에 따라 링커 기호 bar가 다른 인라인 네임 스페이스로 구조체 를 래핑합니다 .

따라서 다음과 같이 작성할 수 있습니다.

#ifndef NDEBUG
inline namespace rel {
#else
inline namespace dbg {
#endif
struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};
}

이제 각 객체의 객체 파일을 보면 릴리스를 사용하고 디버그 플래그를 사용하여 하나를 작성합니다. 링커 심볼에는 인라인 네임 스페이스 이름도 포함되어 있습니다. 이 경우

$ nm rel.o
T__ ZROKfoo9relEbar
$ nm dbg.o
T__ ZROKfoo9dbgEbar

링커 심볼 이름이 다를 수 있습니다.

기호 이름이 존재 rel하고 있음 dbg을 확인하십시오.

이제 디버그를 릴리스 모드로 또는 그 반대로 링크하려고하면 런타임 오류와 반대로 링커 오류가 발생합니다.


답변

실제로 인라인 네임 스페이스에 대한 또 다른 용도를 발견했습니다.

Qt는 , 당신은 몇 가지 여분을 얻을, 좋은 사용하여 기능을 Q_ENUM_NS다시 바깥 쪽 네임 스페이스를 선언하는 Meta Object를 가지고 있어야한다, Q_NAMESPACE. 그러나 Q_ENUM_NS작동 Q_NAMESPACE 하려면 동일한 파일 ⁽¹⁾에 해당 파일이 있어야합니다. 그리고 하나만 있거나 중복 정의 오류가 발생합니다. 이것은 사실상 모든 열거가 동일한 헤더에 있어야 함을 의미합니다. 왝.

또는 … 인라인 네임 스페이스를 사용할 수 있습니다. 에 열거를 숨기면inline namespace메타 개체가 다른 맹 글링 된 이름을 가지지 만 추가 네임 스페이스와 같은 사용자를 찾는 것은 존재하지 않습니다.

따라서 어떤 이유로 든 필요한 경우 하나의 네임 스페이스처럼 보이는 여러 개의 하위 네임 스페이스로 항목을 분할하는 데 유용합니다 . 물론 이것은 using namespace inner외부 네임 스페이스 에 쓰는 것과 비슷 하지만 내부 네임 스페이스의 이름을 두 번 쓰는 DRY 위반이 없습니다 .


  1. 실제로 그것보다 더 나쁩니다. 동일한 중괄호 세트에 있어야합니다.

  2. 메타 객체를 정규화하지 않고 메타 객체에 액세스하려고하지 않는 한 메타 객체는 거의 직접 사용되지 않습니다.


답변

그래서 요점을 요약,하기 using namespace v99inline namespace같은 아니었다 전자는 사용의 문제 해결 C ++ (11)에 도입 된 전용 키워드 (인라인) 이전 버전의 라이브러리에 대한 해결 방법이었다 using같은 버전 관리 기능을 제공하는 동안을. 사용 using namespaceADL에 문제를 야기하는 데 사용 (ADL 지금 따라 나타나지만 using지시를) 및 아웃 오브 라인 등 라이브러리 클래스 / 기능의 전문화 사용자가하는 것은 작품 그 진정한 네임 스페이스의 완료 외부합니다 (이름을한다면 사용자는 몰랐을 것입니다. 즉, 전문화를 해결하려면 B :: 대신 B :: abi_v2 ::를 사용해야합니다.

//library code
namespace B { //library name the user knows
    namespace A { //ABI version the user doesn't know about
        template<class T> class myclass{int a;};
    }
    using namespace A; //pre inline-namespace versioning trick
}

// user code
namespace B { //user thinks the library uses this namespace
    template<> class myclass<int> {};
}

정적 분석 경고가 표시 first declaration of class template specialization of 'myclass' outside namespace 'A' is a C++11 extension [-Wc++11-extensions]됩니다. 그러나 네임 스페이스 A를 인라인으로 만들면 컴파일러가 특수화를 올바르게 해결합니다. C ++ 11 확장을 사용하면 문제가 해결됩니다.

using;을 사용할 때 라인 외부 정의는 해석되지 않습니다 . 중첩 / 중첩되지 않은 확장 네임 스페이스 블록에 선언되어야합니다. 즉, 어떤 이유로 든 함수 자체 구현을 제공하도록 허용 된 경우 사용자는 ABI 버전을 다시 알아야합니다.

#include <iostream>
namespace A {
    namespace B{
        int a;
        int func(int a);
        template<class T> class myclass{int a;};
        class C;
        extern int d;
    }
    using namespace B;
}
int A::d = 3; //No member named 'd' in namespace A
class A::C {int a;}; //no class named 'C' in namespace 'A' 
template<> class A::myclass<int> {}; // works; specialisation is not an out-of-line definition of a declaration
int A::func(int a){return a;}; //out-of-line definition of 'func' does not match any declaration in namespace 'A'
namespace A { int func(int a){return a;};} //works
int main() {
    A::a =1; // works; not an out-of-line definition
}

B를 인라인으로 만들면 문제가 사라집니다.

다른 기능 inline네임 스페이스는 라이브러리 작성자가 라이브러리에 대한 투명한 업데이트를 제공 할 수 있도록합니다. 1) 사용자가 새로운 네임 스페이스 이름으로 코드를 리팩터링하지 않고 2) 자세한 정보 표시 부족 및 3) API 관련이없는 세부 사항의 추상화를 제공합니다. 4) 인라인이 아닌 네임 스페이스를 사용하는 것과 동일한 유익한 링커 진단 및 동작을 제공합니다. 라이브러리를 사용한다고 가정 해 봅시다.

namespace library {
    inline namespace abi_v1 {
        class foo {
        }
    }
}

이를 통해 사용자 library::foo는 설명서에 ABI 버전을 몰라도 포함시킬 필요없이 전화를 걸 수 있습니다 . 사용하면 library::abiverison129389123::foo더러워 보일 것입니다.

foo클래스에 새 멤버 추가와 같은 업데이트가 수행되면 이미 멤버를 사용하지 않으므로 인라인 네임 스페이스 이름을 변경해도 API 레벨에서 아무것도 변경되지 않으므로 API 레벨의 기존 프로그램에 영향을 미치지 않습니다. library::foo여전히 작동 하기 때문 입니다.

namespace library {
    inline namespace abi_v2 {
        class foo {
            //new member
        }
    }
}

그러나 링크 된 프로그램의 경우 인라인 네임 스페이스 이름이 일반 네임 스페이스와 같은 기호 이름으로 맹 글링되므로 변경 사항이 링커에 투명하지 않습니다. 따라서 응용 프로그램을 다시 컴파일하지 않고 새 버전의 라이브러리 abi_v1와 연결하면 실제로 연결되어 ABI 비 호환성으로 인해 런타임에 신비한 논리 오류가 발생하지 않고 심볼 을 찾을 수 없습니다. 새 멤버를 추가하면 컴파일 타임 (API 레벨)에 프로그램에 영향을 미치지 않더라도 유형 정의가 변경되어 ABI 호환성이 발생합니다.

이 시나리오에서 :

namespace library {
    namespace abi_v1 {
        class foo {
        }
    }

    inline namespace abi_v2 {
        class foo {
            //new member
        }
    }
}

2 개의 인라인이 아닌 네임 스페이스를 사용하는 것과 abi_v1같이 전역 심볼 중 하나에서 엉망이되고 올바른 (오래된) 유형 정의를 사용 하기 때문에 애플리케이션을 다시 컴파일 할 필요없이 라이브러리의 새 버전을 링크 할 수 있습니다. 그러나 응용 프로그램을 다시 컴파일하면 참조가 해결됩니다 library::abi_v2.

사용 using namespace은 사용하는 것보다 기능이 적지 만 inline(외부 정의가 해결되지 않는다는 점에서) 위와 동일한 4 가지 장점을 제공합니다. 그러나 실제 문제는 이제 전용 키워드가있을 때 해결 방법을 계속 사용하는 이유입니다. 더 나은 연습, 덜 장황한 (2 대신 1 줄의 코드를 변경해야 함) 의도를 명확하게합니다.


답변