Область видимости
Каждое имя, которое появляется в программе на C++, действительно только в некоторой, возможно, несмежной части исходного кода, называемой его областью видимости.
В пределах области видимости поиск неполного имени можно использовать для связывания имени с его объявлением.
Область видимости блока
Потенциальная область видимости переменной, представленной объявлением в блоке (составной оператор), начинается в точке объявления и заканчивается в конце блока. Фактическая область видимости такая же, как и потенциальная область, если только не существует вложенного блока с объявлением, которое вводит идентичное имя (в этом случае вся потенциальная область видимости вложенного объявления исключается из области видимости внешнего объявления)
int main()
{
int a = 0; // начинается область видимости первой 'a'
++a; // имя 'a' находится в области видимости и ссылается на первую 'a'
{
int a = 1; // начинается область видимости второй 'a'
// область видимости первой 'a' прервана
a = 42; // 'a' входит в область видимости и ссылается на вторую 'a'
} // блок заканчивается, область видимости второй 'a' заканчивается
// область видимости первой 'a' восстанавливается
} // блок заканчивается, область видимости первой 'a' заканчивается
int b = a; // Ошибка: имя 'a' вне области видимости
Потенциальная область видимости имени, объявленного в обработчике исключений, начинается в точке объявления и заканчивается, когда завершается обработчик исключений, и не входит в область видимости другого обработчика исключений или включающего блока.
try {
f();
} catch(const std::runtime_error& re) { // начинается область видимости re
int n = 1; // начинается область видимости n
std::cout << re.what(); // re находится в области видимости
} // область видимости re заканчивается, область видимости n заканчивается
catch(std::exception& e) {
std::cout << re.what(); // ошибка: re находится вне области видимости
++n; // ошибка: n находится вне области видимости
}
Потенциальная область видимости следующих имён начинается в точке объявления и заканчивается в конце контролируемого оператора:
|
(начиная с C++11) |
|
(начиная с C++17) |
Base* bp = new Derived;
if(Derived* dp = dynamic_cast<Derived*>(bp))
{
dp->f(); // dp входит в область видимости
} // область видимости dp заканчивается
for(int n = 0; // область видимости n начинается
n < 10; // n в области видимости
++n) // n в области видимости
{
std::cout << n << ' '; // n в области видимости
} // область видимости n заканчивается
Область видимости параметра функции
Потенциальная область видимости параметра функции (включая параметры лямбда-выражения) или предопределённой переменной, локальной для функции (такой как __func__) (начиная с C++11), начинается с точки её объявления.
- Если ближайший включающий декларатор функции не является декларатором определения функции, его потенциальная область видимости заканчивается в конце этого декларатора функции.
- В противном случае его потенциальная область видимости заканчивается в конце последнего обработчика исключений блока try функции, или в конце тела функции, если блок try функции не использовался.
const int n = 3;
int f1(int n, // область видимости глобальной 'n' прервана,
// область видимости параметра 'n' начинается
int y = n); // ошибка: аргумент по умолчанию ссылается на параметр
int (*(*f2)(int n))[n]; // OK: область видимости параметра функции 'n'
// заканчивается в конце декларатора своей функции
// в деклараторе массива, глобальная n находится в области
// видимости (это объявление указателя на функцию, возвращающую указатель на массив
// из 3-х int)
// напротив
auto (*f3)(int n)->int (*)[n]; // ошибка: параметр 'n' как граница массива
int f(int n = 2) // область видимости 'n' начинается
try // блок try функции
{ // тело функции начинается
++n; // 'n' находится в области видимости и ссылается на параметр функции
{
int n = 2; // область видимости локальной переменной 'n' начинается
// область видимости параметра функции 'n' прервана
++n; // 'n' ссылается на локальную переменную в этом блоке
} // область видимости локальной переменной 'n' прервана
// область видимости параметра функции 'n' возобновляется
} catch(...) {
++n; // n находится в области видимости и ссылается на параметр функции
throw;
} // последний обработчик исключений заканчивается, область видимости параметра
// функции 'n' заканчивается
int a = n; // OK: глобальная 'n' находится в области видимости
Область видимости функции
Метка (и только метка), объявленная внутри функции, находится в области видимости всей этой функции, во всех вложенных блоках, до и после её собственного объявления.
void f()
{
{
goto label; // метка в области видимости, даже если объявлена позже
label:;
}
goto label; // метка игнорирует область видимости блока
}
void g()
{
goto label; // ошибка: метка не входит в область видимости g()
}
Область видимости пространства имён
Потенциальная область видимости любой сущности, объявленной в пространстве имён, начинается с объявления и состоит из конкатенации всех определений пространств имён для одного и того же имени пространства имён, которые следуют за ним, плюс, для любой директивы using, которая ввела имя или всё его пространство имён в другую область видимости, оставшаяся часть этой области.
Область видимости верхнего уровня единицы трансляции ("область видимости файла" или "глобальная область видимости"), также является пространством имён и правильно называется "областью глобального пространства имён". Потенциальная область видимости любой сущности, объявленной в области видимости глобального пространства имён, начинается с объявления и продолжается до конца единицы трансляции.
Имя, объявленное в безымянном пространстве имён или во встроенном пространстве имён, видно во включающем пространстве имён.
namespace N { // область видимости N начинается (как элемент глобального пространства имён)
int i; // область видимости i начинается
int g(int a) { return a; } // область видимости g начинается
int j(); // область видимости j начинается
void q(); // область видимости q начинается
namespace {
int x; // область видимости x начинается
} // область видимости x не заканчивается
inline namespace inl { // область видимости inl начинается
int y; // область видимости y начинается
} // область видимости y не заканчивается
} // область видимости i,g,j,q,inl,x,y прервана
namespace {
int l=1; // область видимости l начинается
} // область видимости l не заканчивается (это элемент безымянного пространства имён)
namespace N { // область видимости i,g,j,q,inl,x,y продолжается
int g(char a) { // перегружает N::g(int)
return l+a; // l из безымянного пространства имён входит в область видимости
}
int i; // ошибка: повторяющееся определение (i уже входит в область видимости)
int j(); // OK: повторное объявления функции разрешено
int j() { // OK: определение ранее объявленной N::j()
return g(i); // вызов N::g(int)
}
int q(); // ошибка: q уже находится в области с другим возвращаемым типом
} // область видимости i,g,j,q,inl,x,y прервана
int main() {
using namespace N; // область видимости i,g,j,q,inl,x,y возобновляется
i = 1; // N::i входит в область видимости
x = 1; // N::(анонимная)::x входит в область видимости
y = 1; // N::inl::y входит в область видимости
inl::y = 2; // N::inl также входит в область видимости
} // область видимости i,g,j,q,inl,x,y прервана
Область видимости класса
Потенциальная область видимости имени, объявленного в классе, начинается в точке объявления и включает в себя остальную часть тела класса и все тела функций (даже если они определены вне определения класса или перед объявлением имени), аргументы по умолчанию, спецификации исключений, внутриклассовые инициализаторы скобки или равенства и всё это во вложенных классах рекурсивно.
class X {
int f(int a = n) { // X::n находится в области видимости внутри параметра по умолчанию
return a*n; // X::n находится в области видимости внутри тела функции
}
using r = int;
r g();
int i = n*2; // X::n находится в области видимости внутри инициализатора
// int x[n]; // Ошибка: n не входит в область видимости тела класса
static const int n = 1;
int x[n]; // OK: n теперь находится в области видимости тела класса
};
//r X::g() { // Ошибка: r не входит в область видимости тела функции-элемента
// вне класса
auto X::g()->r { // OK: конечный возвращаемый тип X::r находится в области видимости
return n; // X::n находится в области видимости тела функции-элемента вне класса
}
Если имя используется в теле класса до его объявления, а другое объявление этого имени находится в области видимости, программа некорректно сформирована, диагностика не требуется.
typedef int c; // ::c
enum { i = 1 }; // ::i
class X {
char v[i]; // Ошибка: в этой точке, i ссылается на ::i,
// но есть ещё X::i
int f() {
return sizeof(c); // OK: X::c, не ::c находится в области видимости внутри функции-элемента
}
char c; // X::c
enum { i = 2 }; // X::i
};
typedef char* T;
struct Y {
T a; // Ошибка: в этой точке, T ссылается на ::T,
// но есть также Y::T
typedef long T;
T b;
};
Имена любых элементов класса могут использоваться только в четырёх контекстах:
- в области видимости своего класса или в области видимости производного класса
- после оператора
., применённого к выражению типа его класса или класса, производного от него - после оператора
->, применённого к выражению типа указателя на его класс или указателей на производный от него класс - после оператора
::, применённого к имени его класса или имени производного от него класса
Область видимости перечисления
Потенциальная область видимости перечислителя перечисления без области видимости начинается в точке объявления и заканчивается в конце охватывающей области видимости.
|
Потенциальная область видимости перечислителя перечисления с областью видимости начинается в точке объявления и заканчивается в конце спецификатора перечисления. |
(начиная с C++11) |
enum e1_t { // перечисление без области видимости
A,
B = A*2
}; // область видимости A и B не заканчивается
enum class e2_t { // перечисление с областью видимости
SA,
SB = SA*2 // SA находится в области видимости
}; // область видимости SA и SB заканчивается
e1_t e1 = B; // OK, B находится в области видимости
// e2_t e2 = SB; // Ошибка: SB не находится в области видимости
e2_t e2 = e2_t::SB; // OK
Область видимости параметров шаблона
Потенциальная область видимости имени параметра шаблона начинается непосредственно в точке объявления и продолжается до конца наименьшего объявления шаблона, в котором он был введён. В частности, параметр шаблона может использоваться в объявлениях последующих параметров шаблона и в спецификациях базовых классов, но не может использоваться в объявлениях предыдущих параметров шаблона.
template< typename T, // область видимости T начинается
T* p, // T может использоваться для параметра, не являющегося типом
class U = T // T может использоваться для типа по умолчанию
>
class X : public Array<T> // T может использоваться в имени базового класса
{
// T можно использовать и внутри тела
}; // область видимости T и U закончилась, область видимости X продолжается
Потенциальная область действия имени параметра шаблонного параметра шаблона, это наименьший список параметров шаблона, в котором появляется это имя
template< template< // шаблонный параметр шаблона
typename Y, // область видимости Y начинается
typename G = Y // Y находится в области видимости
> // область видимости Y и G закончилась
class T,
// typename U = Y // Ошибка: Y находится вне области видимости
typename U
>
class X
{
}; // область видимости T и U закончилась
Подобно другим вложенным областям видимости, имя параметра шаблона скрывает то же имя из внешней области видимости на время своего существования:
typedef int N;
template< N X, // параметр не тип типа int
typename N, // область видимости этого N начинается, область видимости
// ::N прервана
template<N Y> class T // N здесь параметр шаблона, а не int
> struct A;
Точка объявления
Область видимости начинается с точки объявления, которая расположена следующим образом:
Для переменных и других имён, представленных простыми объявлениями, точка объявления находится сразу после объявления имени и перед его инициализатором, если таковой имеется:
int x = 32; // внешний x находится в области видимости
{
int x = x; // внутренний x находится в области видимости до инициализатора (= x)
// при этом внутренний x не инициализируется значением внешнего x (32),
// при этом внутренний x инициализируется собственным (неопределённым)
// значением
}
std::function<int(int)> f = [&](int n){return n>1 ? n*f(n-1) : n;};
// имя функции 'f' находится в области видимости лямбда и
// может быть правильно захвачено по ссылке, делая вызов функции рекурсивным
const int x = 2; // область видимости первого 'x' начинается
{
int x[x] = {}; // область видимости второго x начинается до инициализатора (= {}),
// но после декларатора (x[x]). Внутри декларатора внешний
// 'x' всё ещё в области видимости. Это объявляет массив из 2-х int.
}
Точка объявления класса или шаблона класса находится сразу после идентификатора, который именует класс (или идентификатор шаблона, который именует специализацию шаблона), появляется в его заголовочном файле класса, и уже находится в области видимости в списке базовых классов:
// S находится в области видимости через двоеточие
struct S: std::enable_shared_from_this<S> {};
Положение спецификатор перечисления или объявления непрозрачного перечисления (начиная с C++11) находится сразу после идентификатора, который именует перечисление.
enum E : int { // E уже в области видимости
A = sizeof(E)
};
Точка объявления псевдонима типа или псевдонима шаблона находится сразу после идентификатора типа, на который ссылается псевдоним:
using T = int; // точка объявления T находится в точке с запятой
using T = T; // то же, что и T = int
Точка объявления декларатора в объявлении using, которое не именует конструктор, находится сразу после декларатора:
template<int N>
class base {
protected:
static const int next = N + 1;
static const int value = N;
};
struct derived : base<0>, base<1>, base<2> {
using base<0>::next, // next теперь в области видимости
base<next>::value; // derived::value равно 1
};
Точка объявления перечислителя находится сразу после его определения (а не перед инициализатором, как для переменных):
const int x = 12;
{
enum { x = x + 1, // точка объявления находится в запятой, x инициализируется
// значением 13
y = x + 1 // перечислитель x теперь находится в области видимости,
// y инициализируется значением 14
};
}
Точка объявления имени внедрённого класса следует сразу за открывающей скобкой в определении его класса (или шаблона класса):
template<typename T>
struct Array
// : std::enable_shared_from_this<Array> // Ошибка: имя внедрённого класса не входит в
// область видимости
: std::enable_shared_from_this< Array<T> > //OK: имя шаблона Array находится в
// области видимости
{ // внедрённое имя класса Array теперь находится в области видимости, как если бы имя
// открытого элемента
Array* p; // указатель на Array<T>
};
|
Точка объявления локальной для функции предопределённой переменной |
(начиная с C++11) |
|
Точка объявления структурной привязки находится сразу после списка идентификаторов объявления структурной привязки, но инициализаторам структурной привязки запрещено ссылаться на любое из вводимых имён. |
(начиная с C++17) |
|
Точка объявления переменной или структурной привязки (начиная с C++17), объявленных в объявлении_диапазона основанного на диапазоне оператора std::vector<int> x;
for (auto x : x) { // вектор x находится в области видимости перед
// закрывающей скобкой, auto x находится в
// области видимости в закрывающей скобке
}
|
(начиная с C++11) |
Точка объявления параметра шаблона находится сразу после полного параметра шаблона (включая необязательный аргумент по умолчанию):
typedef unsigned char T;
template<class T
= T // поиск находит имя typedef unsigned char
, T // поиск находит параметр шаблона
N = 0> struct A { };
| Этот раздел не завершён Причина: остаток [basic.scope.pdecl] |
Ссылки
- C++23 стандарт (ISO/IEC 14882:2023):
- 6.4 Область видимости [basic.scope]
- C++20 стандарт (ISO/IEC 14882:2020):
- 6.4 Область видимости [basic.scope]
- C++17 стандарт (ISO/IEC 14882:2017):
- 6.3 Область видимости [basic.scope]
- C++14 стандарт (ISO/IEC 14882:2014):
- 3.3 Область видимости [basic.scope]
- C++11 стандарт (ISO/IEC 14882:2011):
- 3.3 Область видимости [basic.scope]
- C++98 стандарт (ISO/IEC 14882:1998):
- 3.3 Декларативные регионы и области видимости [basic.scope]
Смотрите также
Документация C по Область видимости
|