이름이없는 / 익명 네임 스페이스 대 정적 함수


507

C ++의 기능은 다음과 같이 명명되지 않은 (익명) 네임 스페이스를 만들 수있는 기능입니다.

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

네임 스페이스의 이름을 지정할 수 없으므로 외부에서 액세스 할 수 없습니다. 그러나 명명되지 않은 네임 스페이스 생성 된 파일 내에서 암시적인 using-clause가있는 것처럼 액세스 할 수 있습니다.

내 질문은 정적 함수를 사용하는 것보다 왜 또는 언제 이것이 좋을까요? 아니면 본질적으로 똑같은 일을하는 두 가지 방법입니까?


13
C ++ 11 static에서이 문맥에서 의 사용법은 사용되지 않았다 ; 하지만 익명의 네임 스페이스에 우수한 대안입니다static , 거기 때 실패 할 경우 인스턴스를 static구출하러 온다 .
legends2k

답변:


332

C ++ 표준은 섹션 7.3.1.1의 네임 스페이스 네임 스페이스, 단락 2를 읽습니다.

네임 스페이스 범위에서 객체를 선언 할 때는 정적 키워드를 사용하지 않으므로 unnamed-namespace는 탁월한 대안을 제공합니다.

정적은 유형 선언이 아닌 객체, 함수 및 익명 공용체의 이름에만 적용됩니다.

편집하다:

정적 키워드 (번역 단위에서 변수 선언의 가시성에 영향을 미침)를 사용하지 않기로 한 결정이 취소되었습니다 ( ref ). 이 경우 정적 네임 스페이스 나 명명되지 않은 네임 스페이스를 사용하면 본질적으로 정확히 동일한 작업을 수행하는 두 가지 방법으로 돌아갑니다. 자세한 내용은 SO 질문을 참조하십시오 .

이름이없는 네임 스페이스는 여전히 번역 단위 로컬 형식을 정의 할 수있는 이점이 있습니다. 자세한 내용은 SO 질문을 참조하십시오.

크레딧은 Mike Percy 에게이 사실을 알려주었습니다.


39
Head Geek는 함수에 대해서만 사용되는 정적 키워드에 대해 묻습니다. 네임 스페이스 범위에 선언 된 엔터티에 적용된 정적 키워드는 내부 연결을 지정합니다. 익명 네임 스페이스로 선언 된 엔터티는 외부 연결 (C ++ / 3.5)을 갖지만 고유 한 이름의 범위 내에 있어야합니다. 명명되지 않은 네임 스페이스의 익명 성은 선언을 효과적으로 숨겨 번역 단위 내에서만 액세스 할 수있게합니다. 후자는 static 키워드와 동일한 방식으로 효과적으로 작동합니다.
mloskot

5
외부 연결의 단점은 무엇입니까? 이것이 인라인에 영향을 줄 수 있습니까?
Alex

17
정적 키워드가 더 이상 사용되지 않는다고 C ++ 디자인에서 인용 한 사람들은 아마도 큰 실제 시스템에서 거대한 C 코드로 작업하지 않았을 것입니다 ... 블록).
Calmarius

23
이 답변은 Google에서 "c ++ anonymous namespace"의 최상위 결과로 나오므로 정적 사용은 더 이상 사용되지 않습니다. 자세한 내용은 stackoverflow.com/questions/4726570/…open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1012 를 참조하십시오.
Michael Percy

2
@ErikAronesty 잘못 들리 네요. 재현 가능한 예가 있습니까? C ++ 11에서-심지어 그 이전에도 일부 컴파일러에서-명명되지 않은 namespaces에는 암시 적으로 내부 연결이 있으므로 차이가 없어야합니다. C ++ 11에서는이 기능을 요구 사항으로 지정하여 이전에 잘못된 표현으로 인해 발생했을 수있는 모든 문제를 해결했습니다.
underscore_d

73

익명 네임 스페이스에 메서드를 넣으면 실수로 하나의 정의 규칙을 위반 하지 않으므로 링크 할 수있는 다른 메서드와 동일하게 도우미 메서드의 이름을 지정할 필요가 없습니다.

그리고 luke가 지적한 것처럼 표준에서는 정적 네임 스페이스보다 익명 네임 스페이스를 선호합니다.


2
정적 멤버 함수가 아닌 정적 독립형 함수 (예 : 파일 범위 함수)를 언급하고있었습니다. 정적 독립형 함수는 명명되지 않은 네임 스페이스의 함수와 거의 동일하므로 문제가됩니다.
Geek

2
아; 글쎄, ODR은 여전히 ​​적용됩니다. 단락을 제거하도록 편집되었습니다.
hazzen

내가 알다시피 정적 함수에 대한 ODR은 헤더에 정의되어 있고이 헤더가 둘 이상의 번역 단위에 포함되어 있으면 작동하지 않습니다. 이 경우 같은 기능의 여러 사본을받습니다
Andriy Tylychko

@Andy T : 헤더가 포함 된 경우 "여러 정의"가 표시되지 않습니다. 전처리 기가 처리합니다. 전처리 기가 생성 한 출력을 연구 할 필요가 없다면 다소 이국적이고 드문 것 같습니다. 또한 "#ifndef SOME_GUARD-#define SOME_GUARD ..."와 같이 헤더 파일에 "guards"를 포함시키는 것이 좋습니다. 이는 전처리 기가 동일한 헤더를 두 번 포함하지 않도록합니다.
Nikita Vorontsov

가드 @NikitaVorontsov 가드가 동일한 번역 단위에 동일한 헤더를 포함하는 것을 막을 수 있지만, 다른 번역 단위에서 다중 정의를 허용합니다. 이로 인해 "다중 정의"링커 오류가 발생할 수 있습니다.
Alex

37

static이 놀라운 효과를 갖는 한 가지 경우가 있습니다 (적어도 나에게있었습니다). 14.6.4.2/1의 C ++ 03 표준 상태는 다음과 같습니다.

템플릿 매개 변수에 의존하는 함수 호출의 경우 함수 이름이 규정되지 않은 id 이지만 template-id가 아닌 경우 후보 함수는 다음을 제외하고 일반적인 조회 규칙 (3.4.1, 3.4.2)을 사용하여 찾습니다.

  • 규정되지 않은 이름 조회 (3.4.1)를 사용하는 조회의 경우 템플리트 정의 컨텍스트에서 외부 링크가있는 함수 선언 만 발견됩니다.
  • 연관된 네임 스페이스 (3.4.2)를 사용하는 조회의 경우 템플리트 정의 컨텍스트 또는 템플리트 인스턴스화 컨텍스트에서 외부 링크가있는 함수 선언 만 찾을 수 있습니다.

...

아래 코드는 예상대로 호출 foo(void*)되지 않습니다 foo(S const &).

template <typename T>
int b1 (T const & t)
{
  foo(t);
}

namespace NS
{
  namespace
  {
    struct S
    {
    public:
      operator void * () const;
    };

    void foo (void*);
    static void foo (S const &);   // Not considered 14.6.4.2(b1)
  }

}

void b2()
{
  NS::S s;
  b1 (s);
}

그 자체로는 그다지 큰 문제는 아니지만 완전히 호환되는 C ++ 컴파일러 (예 : 지원 export하는 static키워드 )의 경우 키워드는 다른 방식으로는 사용할 수없는 기능을 여전히 가지고 있음을 강조합니다 .

// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
  foo(t);
}

// foo.cc
#include "bar.h"
namespace NS
{
  namespace
  {
    struct S
    {
    };

    void foo (S const & s);  // Will be found by different TU 'bar.cc'
  }
}

void b2()
{
  NS::S s;
  b1 (s);
}

명명되지 않은 네임 스페이스의 함수가 ADL을 사용하는 템플릿에서 찾을 수 없도록하는 유일한 방법은 그것을 만드는 것 static입니다.

Modern C ++ 업데이트

C ++ '11에서 이름이없는 네임 스페이스의 멤버는 내부적으로 내부적으로 연결되어 있습니다 (3.5 / 4).

명명되지 않은 네임 스페이스 또는 명명되지 않은 네임 스페이스 내에서 직접 또는 간접적으로 선언 된 네임 스페이스에는 내부 연결이 있습니다.

그러나 동시에 14.6.4.2/1은 연결에 대한 언급을 제거하도록 업데이트되었습니다 (C ++ '14에서 가져옴).

postfix-expression이 종속 이름 인 함수 호출의 경우 후보 함수는 다음을 제외하고 일반적인 조회 규칙 (3.4.1, 3.4.2)을 사용하여 찾습니다.

  • 규정되지 않은 이름 조회 (3.4.1)를 사용하는 조회의 경우 템플리트 정의 컨텍스트의 함수 선언 만 발견됩니다.

  • 연관된 네임 스페이스 (3.4.2)를 사용하는 조회의 경우 템플리트 정의 컨텍스트 또는 템플리트 인스턴스화 컨텍스트에서 찾은 함수 선언 만 발견됩니다.

결과적으로 정적 네임 스페이스 멤버와 이름이없는 네임 스페이스 멤버 간의 이러한 특정 차이점이 더 이상 존재하지 않습니다.


3
수출 키워드는 냉정한 것이 아닙니까? "수출"을 지원하는 유일한 컴파일러는 실험들, 그리고 놀라움하지 않는 한, "수출"도 (이 예상 하였다하고 있지 이외에) 때문에 예상치 못한 부작용의 다른 구현되지 않습니다
paercebal

2
서브 젯에있는 Herb Sutter의 기사 : gotw.ca/publications/mill23-x.htm
paercebal

3
Edison Design Group (EDG)의 프론트 엔드는 실험적인 것입니다. 거의 확실하게 세계에서 가장 표준을 따르는 C ++ 구현입니다. 인텔 C ++ 컴파일러는 EDG를 사용합니다.
Richard Corden

1
어떤 C ++ 기능에 '예기치 않은 부작용'이 없는가? 내보내기의 경우 이름이 지정되지 않은 네임 스페이스 함수가 ​​다른 TU에서 발견됩니다. 이는 템플리트 정의를 직접 포함시킨 것과 같습니다. 이렇지 않으면 더 놀라운 일입니다!
Richard Corden

나는 당신이 거기에 오타가 있다고 생각합니다- NS::S일하기 위해, S안에있을 필요는 namespace {}없습니까?
Eric

12

최근에 코드에서 정적 키워드를 익명 네임 스페이스로 교체하기 시작했지만 네임 스페이스의 변수를 더 이상 디버거에서 검사 할 수없는 문제가 발생했습니다. VC60을 사용하고 있었으므로 다른 디버거에서 문제가 아닌지 알 수 없습니다. 내 해결 방법은 '모듈'네임 스페이스를 정의하여 cpp 파일의 이름을 지정하는 것입니다.

예를 들어, XmlUtil.cpp 파일에서 XmlUtil_I { ... }모든 모듈 변수 및 함수에 대한 네임 스페이스 를 정의 합니다. 그렇게 XmlUtil_I::하면 디버거에 자격을 적용 하여 변수에 액세스 할 수 있습니다. 이 경우 다른 곳에서 사용하려는 _I공용 네임 스페이스와 구별됩니다 XmlUtil.

익명의 접근 방식과 비교할 때이 접근법의 잠재적 단점은 다른 모듈에서 네임 스페이스 한정자를 사용하여 원하는 정적 범위를 위반 할 수 있다는 것입니다. 그래도 그것이 중요한 관심사인지 모르겠습니다.


7
이 작업도 수행했지만을 사용 #if DEBUG namespace BlahBlah_private { #else namespace { #endif하여 "모듈 네임 스페이스"는 디버그 빌드에만 존재하며 익명 네임 스페이스는 그렇지 않으면 사용됩니다. 디버거가 이것을 처리하는 좋은 방법을 제공한다면 좋을 것입니다. Doxygen도 혼란스러워합니다.
Kristopher Johnson

4
명명되지 않은 네임 스페이스는 실제로 정적을 대체 할 수있는 대안이 아닙니다. static은 "실제로는 TU의 링크 된 ouside를 얻지 못함"을 의미합니다. 명명되지 않은 네임 스페이스는 "TU 외부에있는 상위 클래스에서 호출되는 경우 임의 이름으로 여전히 내보내

7

C ++ 98 표준에서는 이러한 목적으로 정적 키워드를 사용하지 않습니다. static의 문제는 유형 정의에 적용되지 않는다는 것입니다. 또한 다른 컨텍스트에서 다른 방식으로 사용되는 오버로드 된 키워드이므로 명명되지 않은 네임 스페이스는 일을 조금 단순화합니다.


1
단일 변환 단위에서만 유형을 사용하려면 .cpp 파일 내에 유형을 선언하십시오. 어쨌든 다른 번역 단위에서는 액세스 할 수 없습니다.
Calmarius

4
당신은 생각하지 않습니까? 그러나 동일한 응용 프로그램의 다른 번역 단위 (= cpp-file)가 동일한 이름을 가진 유형을 선언하면 디버그하기 어려운 문제가 있습니다 :-). 예를 들어, 한 유형의 vtable이 다른 유형의 메소드를 호출 할 때 사용되는 상황이 발생할 수 있습니다.
avl_sweden 10

1
더 이상 사용되지 않습니다. 그리고 형식 defs는 내 보내지 않으므로 의미가 없습니다. 정적은 독립형 함수 및 전역 변수에 유용합니다. 명명되지 않은 네임 스페이스는 클래스에 유용합니다.
Erik Aronesty

6

경험상 필자는 이전에 정적 함수를 익명 네임 스페이스에 넣는 것이 C ++ 방법이지만 이전 컴파일러는 때때로 이것에 문제가있을 수 있습니다. 나는 현재 대상 플랫폼을 위해 몇 개의 컴파일러로 작업하고 있으며 더 현대적인 Linux 컴파일러는 익명 네임 스페이스에 함수를 배치하는 것이 좋습니다.

그러나 지정되지 않은 이후 릴리스까지는 Solaris에서 실행되는 이전 컴파일러가 때때로이를 받아들이고 다른 경우에는 오류로 표시합니다. 이 오류는 그것이 무엇을, 저를 걱정 것이 아니다 수도 그 때 일을 받아 그것을. 따라서 우리가 전반적으로 현대화 될 때까지 익명 네임 스페이스를 선호하는 정적 (보통 클래스 범위) 함수를 계속 사용합니다.


3

또한이 예제와 같이 변수에 정적 키워드를 사용하는 경우 :

namespace {
   static int flag;
}

매핑 파일에서 볼 수 없습니다


7
그런 다음 익명 네임 스페이스가 전혀 필요하지 않습니다.
Calmarius

2

익명 네임 스페이스와 정적 함수의 컴파일러 별 차이점은 다음 코드를 컴파일하는 것을 볼 수 있습니다.

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

VS 2017으로이 코드를 컴파일하면 ( 경고 C4505 : 참조되지 않은 로컬 함수가 제거 되도록 레벨 4 경고 플래그 / W4 지정 ) 및 -Wunused-function 또는 -Wall 플래그와 함께 gcc 4.9는 VS 2017이 사용하지 않는 정적 함수 clang 3.3 이상뿐만 아니라 gcc 4.9 이상은 네임 스페이스에서 참조되지 않은 함수에 대한 경고와 사용되지 않은 정적 함수에 대한 경고를 생성합니다.

gcc 4.9 및 MSVC 2017의 라이브 데모


2

개인적으로 나는 다음과 같은 이유로 이름없는 네임 스페이스보다 정적 함수를 선호합니다.

  • 함수 정의만으로는 컴파일 된 번역 단위 전용이라는 것은 분명합니다. 네임 스페이스가없는 네임 스페이스를 사용하면 함수가 네임 스페이스에 있는지 스크롤하고 검색해야 할 수 있습니다.

  • 네임 스페이스의 함수는 일부 (이전) 컴파일러에 의해 extern으로 취급 될 수 있습니다. VS2017에서는 여전히 인턴입니다. 이러한 이유로 이름없는 네임 스페이스에 함수가 있어도 여전히 정적으로 표시 할 수 있습니다.

  • 정적 함수는 C 또는 C ++에서 매우 유사하게 동작하지만 이름없는 네임 스페이스는 분명히 C ++ 전용입니다. 이름없는 네임 스페이스는 들여 쓰기에 추가 레벨을 추가하며 나는 그것을 좋아하지 않습니다 :)

따라서 함수에 static을 사용하는 것이 더 이상 사용되지 않는다는 것을 알게되어 기쁩니다 .


익명 네임 스페이스의 함수에는 외부 연결이 있어야합니다. 그들은 그들을 독특하게 만들기 위해 엉망입니다. static실제로 키워드 만 함수에 로컬 링크를 적용합니다. 또한 열렬한 미치광이가 실제로 네임 스페이스에 들여 쓰기를 추가합니까?
Roflcopter4

0

귀하의 질문을 읽는 동안 지금 만이 기능에 대해 배웠으므로 추측 만 할 수 있습니다. 이것은 파일 레벨 정적 변수에 비해 몇 가지 장점을 제공하는 것으로 보입니다.

  • 익명 네임 스페이스는 서로 중첩 될 수 있으므로 심볼을 이스케이프 할 수없는 여러 수준의 보호 기능을 제공합니다.
  • 여러 익명 네임 스페이스를 동일한 소스 파일에 배치하여 동일한 파일 내에서 다른 정적 수준 범위를 만들 수 있습니다.

누군가가 실제 네임 스페이스에서 익명 네임 스페이스를 사용한 경우 학습에 관심이 있습니다.


4
좋은 추측이지만 잘못되었습니다. 이러한 네임 스페이스의 범위는 파일 전체입니다.
Konrad Rudolph

다른 네임 스페이스 내에 익명 네임 스페이스를 정의하는 경우 파일 이름만으로도 해당 네임 스페이스 내에있는 것으로 만 볼 수 있습니다. 시도 해봐.
Greg Rogers

나는 틀릴 수 있지만, 파일 전체가 아니라고 생각합니다 . 익명 네임 스페이스 다음 의 코드에서만 액세스 할 수 있습니다 . 이것은 미묘한 일이며 일반적으로 여러 익명 네임 스페이스로 소스를 오염시키고 싶지 않습니다 ... 여전히 이것은 사용할 수 있습니다.
paercebal

0

차이점은 맹 글링 된 식별자의 이름 ( _ZN12_GLOBAL__N_11bEvs _ZL1b, 실제로는 중요하지 않지만 둘 다 기호 테이블의 로컬 기호로 조합됩니다 ( .globalasm 지시어 없음).

#include<iostream>
namespace {
   int a = 3;
}

static int b = 4;
int c = 5;

int main (){
    std::cout << a << b << c;
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_11aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4
_ZL1b:
        .long   4
        .globl  c
        .align 4
        .type   c, @object
        .size   c, 4
c:
        .long   5
        .text

중첩 된 익명 네임 스페이스는 다음과 같습니다.

namespace {
   namespace {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_112_GLOBAL__N_11aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4

번역 단위의 모든 1 단계 익명 네임 스페이스가 서로 결합되고, 번역 단위의 모든 2 단계 중첩 익명 네임 스페이스가 서로 결합됩니다.

익명 네임 스페이스에 중첩 된 (인라인) 네임 스페이스를 가질 수도 있습니다.

namespace {
   namespace A {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11A1aE, @object
        .size   _ZN12_GLOBAL__N_11A1aE, 4
_ZN12_GLOBAL__N_11A1aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4

which for the record demangles as:
        .data
        .align 4
        .type   (anonymous namespace)::A::a, @object
        .size   (anonymous namespace)::A::a, 4
(anonymous namespace)::A::a:
        .long   3
        .align 4
        .type   b, @object
        .size   b, 4

익명 인라인 네임 스페이스를 가질 수도 있지만 inline익명 네임 스페이스에서는 0의 효과가 있습니다.

inline namespace {
   inline namespace {
       int a = 3;
    }
}

_ZL1b: _Z이것은 맹 글링 된 식별자임을 의미합니다. L는를 통해 지역 기호임을 의미합니다 static. 1식별자의 길이 b와 식별자b

_ZN12_GLOBAL__N_11aE _Z이것은 엉망인 식별자임을 의미합니다. N이 네임 스페이스는 수단 12익명 공간 이름의 길이 _GLOBAL__N_1, 그 익명 공간 이름 _GLOBAL__N_1다음 1식별자의 길이 a, a식별자 인 aE식별자를 폐쇄하는 공간에있다.

_ZN12_GLOBAL__N_11A1aE 다른 네임 스페이스 수준이 있다는 것을 제외하고는 위와 동일합니다. 1A

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.