Qualified name lookup
qualified
이름은 범위 지정 연산자
::
의 오른쪽에 나타나는 이름입니다
(참조:
qualified identifiers
).
qualified 이름은 다음을 참조할 수 있습니다
- 클래스 멤버 (정적 및 비정적 함수, 타입, 템플릿 등 포함),
- 네임스페이스 멤버 (다른 네임스페이스 포함),
- 열거자.
::
의 왼쪽에 아무것도 없으면, lookup은
전역 네임스페이스 스코프
에 있는 선언만 고려합니다. 이는 해당 이름들이 지역 선언에 의해 가려져 있는 경우에도 그러한 이름들을 참조할 수 있게 합니다:
오른쪽 피연산자에 있는 이름에 대해 이름 검색(name lookup)이 수행되기 전에, 왼쪽 피연산자에 있는 이름에 대한 검색이 완료되어야 합니다(
decltype
표현식이 사용되거나 왼쪽에 아무것도 없는 경우는 제외). 이 검색은 해당 이름 왼쪽에 또 다른
::
가 있는지 여부에 따라 한정된(qualified) 또는 비한정된(unqualified) 검색일 수 있으며, 네임스페이스, 클래스 타입, 열거형(enumeration), 그리고 특수화가 타입인 템플릿만을 고려합니다. 왼쪽에서 발견된 이름이 네임스페이스나 클래스, 열거형, 종속 타입(dependent type)을 지정하지 않으면 프로그램은 올바르지 않습니다(ill-formed):
struct A { static int n; }; int main() { int A; A::n = 42; // OK: :: 왼쪽의 비한정 이름 검색은 변수를 무시함 A b; // 오류: A의 비한정 이름 검색이 변수 A를 찾음 } template<int> struct B : A {}; namespace N { template<int> void B(); int f() { return B<0>::n; // 오류: N::B<0>은 타입이 아님 } }
한정된 이름이 선언자 로 사용될 때, 동일한 선언자 내에서 해당 한정된 이름 뒤에 오는 이름들에 대한 비한정 이름 검색 은 해당 멤버의 클래스나 네임스페이스 범위에서 수행되지만, 그 앞에 오는 이름들에 대해서는 수행되지 않습니다:
class X {}; constexpr int number = 100; struct C { class X {}; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // 오류: X를 찾을 때 ::X를 찾고 C::X를 찾지 않음 C::X C::arr[number], brr[number]; // 정상: arr의 크기는 50, brr의 크기는 100
만약
::
다음에
~
문자가 오고, 그 다음에 식별자가 오는 경우(즉, 소멸자나 의사 소멸자를 지정하는 경우), 해당 식별자는
::
왼쪽에 있는 이름과 동일한 범위에서 조회됩니다.
struct C { typedef int I; }; typedef int I1, I2; extern int *p, *q; struct A { ~A(); }; typedef A AB; int main() { p->C::I::~I(); // ~ 다음의 이름 I는 :: 앞의 I와 동일한 범위에서 조회됩니다 // (즉, C의 범위 내에서 조회하므로 C::I를 찾습니다) q->I1::~I2(); // 이름 I2는 I1과 동일한 범위에서 조회됩니다 // (즉, 현재 범위에서 조회하므로 ::I2를 찾습니다) AB x; x.AB::~AB(); // ~ 다음의 이름 AB는 :: 앞의 AB와 동일한 범위에서 조회됩니다 // (즉, 현재 범위에서 조회하므로 ::AB를 찾습니다) }
열거자왼쪽 피연산자 이름의 조회 결과가 열거형 (범위가 지정되었거나 지정되지 않은)인 경우, 오른쪽 피연산자의 조회 결과는 해당 열거형에 속하는 열거자여야 합니다. 그렇지 않으면 프로그램은 잘못된 형식입니다. |
(C++11부터) |
클래스 멤버
왼쪽 피연산자 이름의 조회가 클래스/구조체 또는 공용체 이름으로 확인되면,
::
오른쪽 피연산자의 이름은 해당 클래스의 범위에서 조회되며(따라서 해당 클래스나 그 기본 클래스의 멤버 선언을 찾을 수 있음), 다음과 같은 예외가 적용됩니다:
- 소멸자는 위에서 설명한 대로 (:: 왼쪽에 있는 이름의 범위에서) 조회됩니다.
- 사용자 정의 변환 함수 이름의 conversion-type-id는 먼저 클래스 범위에서 조회됩니다. 찾을 수 없는 경우, 현재 범위에서 이름을 조회합니다.
- 템플릿 인수에서 사용되는 이름들은 현재 범위에서 조회됩니다 (템플릿 이름의 범위가 아닙니다).
- using-선언 에서의 이름들은 동일한 범위에서 선언된 변수, 데이터 멤버, 함수 또는 열거자 이름에 의해 가려진 클래스/열거형 이름들도 고려합니다.
|
이 섹션은 불완전합니다
이유: 위 항목에 대한 세부 예시 부족 |
::
의 우변에서 명시하는 클래스가 좌변과 동일한 경우, 해당 이름은 그 클래스의
생성자
를 지칭합니다. 이러한 한정된 이름은 생성자 선언과
using 선언
에서
상속 생성자
를 위해 사용될 수 있습니다. 함수 이름이 무시되는 조회 상황(즉,
::
의 좌측에서 이름을 조회할 때,
정교화된 타입 지정자
에서 이름을 조회할 때, 또는
기반 클래스 지정자
에서 조회할 때)에서는 동일한 구문이 주입된 클래스 이름으로 해석됩니다:
struct A { A(); }; struct B : A { B(); }; A::A() {} // A::A는 생성자를 지칭하며, 선언에서 사용됨 B::B() {} // B::B는 생성자를 지칭하며, 선언에서 사용됨 B::A ba; // B::A는 타입 A를 지칭함 (B의 스코프에서 조회됨) A::A a; // 오류: A::A는 타입을 지칭하지 않음 struct A::A a2; // OK: 정교화된 타입 지정자에서의 조회는 함수를 무시함 // 따라서 A::A는 단순히 A 스코프 내에서 보이는 클래스 A를 지칭함 // (즉, 주입된 클래스 이름)
한정된 이름 조회(Qualified name lookup)는 중첩 선언이나 파생 클래스에 의해 가려진 클래스 멤버에 접근하는 데 사용될 수 있습니다. 한정된 멤버 함수에 대한 호출은 절대 가상(virtual)이 아닙니다:
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // D::foo 호출 (가상 디스패치) b.B::foo(); // B::foo 호출 (정적 디스패치) }
네임스페이스 멤버
::
왼쪽의 이름이 네임스페이스를 참조하거나
::
왼쪽에 아무것도 없는 경우(이 경우 전역 네임스페이스를 참조함),
::
오른쪽에 나타나는 이름은 해당 네임스페이스의 스코프에서 조회되지만, 다음 경우는 예외입니다.
- 템플릿 인자에 사용된 이름들은 현재 스코프에서 조회됩니다:
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // 오류: X는 N::X가 아닌 ::X로 조회됨
네임스페이스
N
범위 내에서의 정규화된 조회는 먼저
N
에 위치한 모든 선언과
N
의
인라인 네임스페이스 멤버
에 위치한 모든 선언(그리고 전이적으로, 그들의 인라인 네임스페이스 멤버들)을 고려합니다. 해당 집합에 선언이 없는 경우,
N
와
N
의 모든 전이적 인라인 네임스페이스 멤버들에서 발견되는
using-directives
로 명명된 모든 네임스페이스들의 선언들을 고려합니다. 이 규칙들은 재귀적으로 적용됩니다:
int x; namespace Y { void f(float); void h(int); } namespace Z { void h(double); } namespace A { using namespace Y; void f(int); void g(int); int i; } namespace B { using namespace Z; void f(char); int i; } namespace AB { using namespace A; using namespace B; void g(); } void h() { AB::g(); // AB를 검색하고, 조회를 통해 AB::g를 찾아 선택됨 AB::g(void) // (A와 B는 검색되지 않음) AB::f(1); // 먼저 AB를 검색. f가 없음 // 그 다음 A, B를 검색 // 조회를 통해 A::f, B::f를 찾음 // (하지만 Y는 검색되지 않으므로 Y::f는 고려되지 않음) // 오버로드 해결이 A::f(int)를 선택 AB::x++; // 먼저 AB를 검색. x가 없음 // 그 다음 A, B를 검색. x가 없음 // 그 다음 Y와 Z를 검색. 여전히 x가 없음: 이는 오류 AB::i++; // AB를 검색. i가 없음 // 그 다음 A, B를 검색. 조회를 통해 A::i와 B::i를 찾음: 이는 오류 AB::h(16.8); // 먼저 AB를 검색. h가 없음 // 그 다음 A, B를 검색. h가 없음 // 그 다음 Y와 Z를 검색 // 조회를 통해 Y::h와 Z::h를 찾음. 오버로드 해결이 Z::h(double)을 선택 }
동일한 선언이 두 번 이상 발견되는 것이 허용됩니다:
namespace A { int a; } namespace B { using namespace A; } namespace D { using A::a; } namespace BD { using namespace B; using namespace D; } void g() { BD::a++; // 정상: B와 D를 통해 동일한 A::a를 찾음 }
|
이 섹션은 불완전합니다
이유: N4861 6.5.3.2[namespace.qual]의 나머지 부분, 해당 예제들을 축약해 보세요 |
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 215 | C++98 |
::
앞의 이름은 클래스 이름이나 네임스페이스
이름이어야 했으므로 템플릿 매개변수는 허용되지 않았음 |
해당 이름은 클래스, 네임스페이스
또는 종속 타입을 지정해야 함 |
| CWG 318 | C++98 |
::
오른쪽이 왼쪽과 동일한 클래스를
지정하는 경우, 한정된 이름은 항상 해당 클래스의 생성자로 간주됨 |
허용 가능한 경우에만 생성자로 지정
(예: 상세 타입 지정자에서는 아님) |