Область видимости
Каждое имя, которое появляется в программе на 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> };
Точка объявления локальной для функции предопределённой переменной __func__ находится непосредственно перед телом функции в определении функции. |
(начиная с C++11) |
Точка объявления структурной привязки находится сразу после списка идентификаторов объявления структурной привязки, но инициализаторам структурной привязки запрещено ссылаться на любое из вводимых имён. |
(начиная с C++17) |
Точка объявления переменной или структурной привязки (начиная с C++17), объявленных в объявлении_диапазона основанного на диапазоне оператора for, находится сразу после выражения_диапазона: 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 по Область видимости
|