Пространства имён
Варианты
Действия

Область видимости

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
 

Каждое имя, которое появляется в программе на 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 находится вне области видимости
}

Потенциальная область видимости следующих имён начинается в точке объявления и заканчивается в конце контролируемого оператора:

  • имена, объявленные в инструкции-инициализации цикла for
  • имена, объявленные в условии цикла for
  • имена, объявленные в объявлении диапазона цикла range-for
(начиная с C++11)
  • имена, объявленные в инструкции-инициализации оператора if statement или switch
(начиная с C++17)
  • имена, объявленные в условии оператора if, оператора switch или цикла while
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 { };

[править] Ссылки

  • 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 по Область видимости