블록 범위 (Block Scope)
객체의 잠재적인 범위는 블록내 선언 된 지점에서 시작하여 블록의 끝에서 끝난다. 중첩된 블럭내 상위 블럭에서 선언 된 것과 동일한 이름으로 선언하는 경우 상위에서 선언 된 것은 무시되고 블럭내 선언 된 것이 사용된다. 이런 방법은 지양해야 하며 다른 이름을 사용한다.
int main()
{
int a = 0; // scope of the first 'a' begins
++a; // the name 'a' is in scope and refers to the first 'a'
{
int a = 1; // scope of the second 'a' begins
// scope of the first 'a' is interrupted
a = 42; // 'a' is in scope and refers to the second 'a'
} // block ends, scope of the second 'a' ends
// scope of the first 'a' resumes
} // block ends, scope of the first 'a' ends
int b = a; // Error: name 'a' is not in scope
예외 핸들러(handler)에 선언 된 이름의 잠재적 범위는 선언 시점에서 시작하여 예외 핸들러가 종료 될 때 끝난다. 다른 예외 핸들러 또는 엔클로징(enclosing, 둘러싸는) 블록은 범위에 있지 않다.
try {
f();
} catch(const std::runtime_error& re) { // scope of re begins
int n = 1; // scope of n begins
std::cout << re.what(); // re is in scope
} // scope of re ends, scope of n ends
catch(std::exception& e) {
std::cout << re.what(); // error: re is not in scope
++n; // error: n is not in scope
}
if 문 또는 switch 문에서 for 문의 조건에서 선언 된 이름의 잠재적 범위, if 문의 조건에서 while 또는 switch 명령문은 선언 시점에서 시작하여 제어 명령문의 끝에서 종료된다.
Base* bp = new Derived;
if(Derived* dp = dynamic_cast<Derived*>(bp))
{
dp->f(); // dp is in scope
} // scope of dp ends
for(int n = 0; // scope of n begins
n < 10; // n is in scope
++n) // n is in scope
{
std::cout << n << ' '; // n is in scope
}
함수 매개 변수 범위 (function parameters scope)
함수 매개 변수 (람다 표현식의 매개 변수 포함) 또는 함수 내 지역 변수의 잠재적인 범위는 선언 지점에서 시작하고 함수 정의 종료에서 끝난다. 잠재적인 범위는 function-try-block의 마지막 예외 핸들러의 끝 또는 함수 try 블록이 사용되지 않은 경우 함수 본문의 끝에서 끝난다.
const int n = 3;
int f1(int n, // scope of global 'n' interrupted,
// scope of the parameter 'n' begins
int y = n); // error: default argument references a parameter
int (*(*f2)(int n))[n]; // OK: the scope of the function parameter 'n'
// ends at the end of its function declarator
// in the array declarator, global n is in scope
// (this declares a pointer to function returning a pointer to an array of 3 int
// by contrast
auto (*f3)(int n)->int (*)[n]; // error: parameter 'n' as array bound
int f(int n = 2) // scope of 'n' begins
try // function try block
{ // the body of the function begins
++n; // 'n' is in scope and refers to the function parameter
{
int n = 2; // scope of the local variable 'n' begins
// scope of function parameter 'n' interrupted
++n; // 'n' refers to the local variable in this block
} // scope of the local variable 'n' ends
// scope of function parameter 'n' resumes
} catch(...) {
++n; // n is in scope and refers to the function parameter
throw;
} // last exception handler ends, scope of function parameter 'n' ends
int a = n; // OK: global 'n' is in scope
함수 범위 (function scope)
함수 내에서 선언 된 레이블은 해당 함수의 모든 위치, 모든 중첩 된 블록에서 자체 선언 전후에 적용된다.
void f()
{
{
goto label; // label in scope even though declared later
label:;
}
goto label; // label ignores block scope
}
void g()
{
goto label; // error: label not in scope in g()
}
네임스페이스 범위 (namespace scope)
네임스페이스에 선언 된 엔터티의 잠재적 범위는 선언에서 시작하며 뒤에 나오는 동일한 네임스페이스 이름에 대한 모든 네임스페이스 정의의 연결과 이름 또는 전체 네임 스페이스를 다른 범위에 도입 한 using 지시문으로 구성된다.
번역 단위의 최상위 범위 ("파일 범위"또는 "전역 범위")도 네임스페이스이며 "전역 네임스페이스 범위"라고 한다. 전역 네임스페이스 범위에서 선언 된 엔터티의 잠재적 범위는 선언에서 시작하여 번역 단위 끝까지 계속된다.
명명되지 않은 네임스페이스 또는 인라인 네임스페이스에 선언 된 엔터티의 범위에는 엔클로징 네임 스페이스가 포함된다.
namespace N { // scope of N begins (as a member of global namespace)
int i; // scope of i begins
int g(int a) { return a; } // scope of g begins
int j(); // scope of j begins
void q(); // scope of q begins
namespace {
int x; // scope of x begins
} // scope of x does not end
inline namespace inl { // scope of inl begins
int y; // scope of y begins
} // scope of y does not end
} // scope of i,g,j,q,inl,x,y interrupted
namespace {
int l=1; // scope of l begins
} // scope of l does not end (it's a member of unnamed namespace)
namespace N { // scope of i,g,j,q,inl,x,y continues
int g(char a) { // overloads N::g(int)
return l+a; // l from unnamed namespace is in scope
}
int i; // error: duplicate definition (i is already in scope)
int j(); // OK: repeat function declaration is allowed
int j() { // OK: definition of the earlier-declared N::j()
return g(i); // calls N::g(int)
}
int q(); // error: q is already in scope with different return type
} // scope of i,g,j,q,inl,x,y interrupted
int main() {
using namespace N; // scope of i,g,j,q,inl,x,y resumes
i = 1; // N::i is in scope
x = 1; // N::(anonymous)::x is in scope
y = 1; // N::inl::y is in scope
inl::y = 2; // N::inl is also in scope
} // scope of i,g,j,q,inl,x,y interrupted
클래스 범위 (class scope)
클래스에 선언 된 이름의 잠재적인 범위는 선언 시점에서 시작하며 클래스 본문의 나머지 부분과 모든 함수 본문 (클래스 정의 외부 또는 이름 선언 전에 정의 된 경우에도), 기본 인수, 예외 사양을 포함한다. 클래스 내 괄호 또는 동일 이니셜 라이저 및 중첩 클래스의 모든 것들을 재귀적으로 포함한다.
class X {
int f(int a = n) { // X::n is in scope inside default parameter
return a*n; // X::n is in scope inside function body
}
using r = int;
r g();
int i = n*2; // X::n is in scope inside initializer
// int x[n]; // Error: n is not in scope in class body
static const int n = 1;
int x[n]; // OK: n is now in scope in class body
};
//r X::g() { // Error: r is not in scope outside of out-of-class member function body
auto X::g()->r { // OK: trailing return type X::r is in scope
return n; // X::n is in scope in out-of-class member function body
}
이름이 선언되기 전에 클래스 본문에서 사용되며 해당 이름에 대한 또 다른 선언이 범위 내에 있으면 프로그램이 잘못 형성되어 진단할 필요가 없다.
typedef int c; // ::c
enum { i = 1 }; // ::i
class X {
char v[i]; // Error: at this point, i refers to ::i
// but there is also X::i
int f() {
return sizeof(c); // OK: X::c, not ::c is in scope inside a member function
}
char c; // X::c
enum { i = 2 }; // X::i
};
typedef char* T;
struct Y {
T a; // Error: at this point, T refers to ::T
// but there is also Y::T
typedef long T;
T b;
};
모든 클래스 멤버의 이름은 네 가지 컨텍스트에서만 사용할 수 있다.
- 자신의 클래스 범위 또는 파생 클래스의 클래스 범위 내에서
- 해당 클래스의 유형 또는 클래스에서 파생 된 클래스의 표현에 적용되는 연산자
- -> 연산자가 클래스에 대한 포인터 유형의 표현식에 적용된 후 또는 클래스에서 파생 된 클래스에 대한 포인터
- :: 연산자가 해당 클래스의 이름 또는 클래스에서 파생 된 클래스의 이름에 적용
열거형 범위 (enumeration scope)
열거형으로 도입된 열거자의 이름은 선언 시점에서 시작하여 열거 지정자의 끝에 끝난다. (대조적으로, 스코프 없는 열거 자는 열거 지정자의 범위 종료 후)
enum e1_t { // unscoped enumeration
A,
B = A*2
}; // scope of A and B does not end
enum class e2_t { // scoped enumeration
SA,
SB = SA*2 // SA is in scope
}; // scope of SA and SB ends
e1_t e1 = B; // OK, B is in scope
// e2_t e2 = SB; // Error: SB is not in scope
e2_t e2 = e2_t::SB; // OK
템플릿 매개변수 범위 (template parameter scope)
템플릿 매개 변수 이름의 잠재적인 범위는 선언 시점에서 즉시 시작하여 도입 된 템플릿 선언의 가장 작은 끝까지 계속된다. 특히 템플릿 매개 변수는 후속 템플릿 매개 변수의 선언과 기본 클래스의 사양에서 사용할 수 있지만 선행 템플릿 매개 변수의 선언에서는 사용할 수 없다.
template< typename T, // scope of T begins
T* p, // T can be used for a non-type parameter
class U = T // T can be used for a default type
>
class X : public Array<T> // T can be used in base class name
{
// T can be used inside the body as well
}; // scopes of T and U end, scope of X continues
템플릿 매개 변수 이름의 잠재적인 범위는 해당 이름이 나타나는 템플릿 매개 변수 목록 중 가장 작다.
template< template< // template template parameter
typename Y, // scope of Y begins
typename G = Y // Y is in scope
> // scopes of Y and G end
class T,
// typename U = Y // Error: Y is not in scope
typename U
>
class X
{
}; // scopes of T and U end
다른 중첩 된 범위와 유사하게 템플릿 매개 변수의 이름은 자체 기간 동안 외부 범위에서 동일한 이름을 숨긴다.
typedef int N;
template< N X, // non-type parameter of type int
typename N, // scope of this N begins, scope of ::N interrupted
template<N Y> class T // N here is the template parameter, not int
> struct A;
선언 지점 (point of declaration)
선언에 의해 생성된 변수 및 기타 이름의 경우 선언 지점은 해당 이름의 선언자 바로 다음이고 선언 지점은 다음과 같다.
unsigned char x = 32; // scope of the first 'x' begins
{
unsigned char x = x; // scope of the second 'x' begins before the initializer (= x)
// this does not initialize the second 'x' with the value 32,
// this initializes the second 'x' with its own,
// indeterminate, value
}
std::function<int(int)> f = [&](int n){return n>1 ? n*f(n-1) : n;};
// the name of the function 'f' is in scope within the lambda, and can
// be correctly captured by reference, giving a recursive function
const int x = 2; // scope of the first 'x' begins
{
int x[x] = {}; // scope of the second x begins before the initializer (= {})
// but after the declarator (x[x]). Within the declarator, the outer
// 'x' is still in scope. This declares an array of 2 int.
}
구조화 된 바인딩의 선언 지점은 구조화 된 바인딩 선언의 식별자 목록 직후이지만 구조화 된 바인딩 초기화는 도입되는 이름 중 하나를 참조하는 것이 금지된다.(C++17 이후)
클래스 또는 템플릿의 선언 지점은 클래스 이름(또는 템플릿 특수화를 지정하는 템플릿 ID)의 식별자가 클래스 헤드에 나타나고 이미 기본 클래스 목록의 범위에 있다.
// the name 'S' is in scope immediately after it appears,
// so it can be used in the list of base classes
struct S: std::enable_shared_from_this<S>
{
};
열거 형 선언의 지점은 열거 지정자 또는 불투명한 열거형 선언에 나타나는 식별자 바로 뒤이다.
enum E : int { // E is already in scope
A = sizeof(E)
};
유형 별칭 또는 별칭 템플릿의 선언 지점은 별칭이 참조하는 유형 ID 바로 다음이다.
using T = int; // point of declaration of T is at the semicolon
using T = T; // same as T = int
열거자의 선언 지점은 그 정의 직후이다(변수에 대한 초기화 이전은 아님).
const int x = 12;
{
enum { x = x + 1, // point of declaration is at the comma, x is initialized to 13
y = x + 1 // the enumerator x is now in scope, y is initialized to 14
};
}
주입 클래스 이름의 선언 지점은 클래스(또는 템플릿 클래스) 정의 시작 바로 다음이다.
template<typename T>
struct Array
// : std::enable_shared_from_this<Array> // Error: the injected class name is not in scope
: std::enable_shared_from_this< Array<T> > //OK: the template-name Array is in scope
{ // the injected class name Array is now in scope as if a public member name
Array* p; // pointer to Array<T>
};
01.02.01. 범위(Scope) - C++ 이야기(A Story of C++) (wikidocs.net)
'프로그래밍 언어 > C++' 카테고리의 다른 글
[C++] atoi (char > int 형변환) / stoi (string > int 형변환) 함수 구현 (0) | 2023.12.11 |
---|---|
[C++] char형 데이터 int형으로 변환하기 (0) | 2023.12.11 |
[C++] string 타입 문자열을 Split (분할)하기 (0) | 2023.12.06 |
[C++] 배열을 함수의 매개변수로 사용 시 주의점 (0) | 2023.11.15 |
C++ 빌드 진행 과정 (0) | 2023.11.10 |