Spec-Zone.ru › C++

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

Каждое объявление, встречающееся в программе на C++, видно только в некоторых, возможно несвязных, областях видимости .

В пределах области видимости для связывания имени с его объявлением можно использовать поиск неквалифицированного имени.

Общие сведения

У каждой программы есть глобальная область видимости , которая содержит всю программу.

Любая другая область видимости S вводится одним из следующих элементов:

  • объявлением
  • параметром в списке параметров
  • оператором
  • обработчиком
  • утверждением контракта
(начиная с C++26)

S всегда находятся в другой области видимости, которая тем самым содержит S.

Внешней областью видимости в некоторой точке программы является любая содержащая её область видимости; наименьшая такая область называется непосредственной областью видимости в этой точке.

Область видимости расположена между точкой программы P и областью видимости S (которая не содержит P), если она сама или содержит S, но не содержит P.

Родительская область видимости любой области видимости S, не являющейся областью видимости параметров шаблона, — это наименьшая область видимости, которая содержит S и не является областью видимости параметров шаблона.

Если не указано иное:

  • Объявление принадлежит непосредственной области видимости в точке его положения.
  • Целевая область видимости объявления — это область видимости, которой оно принадлежит.
  • Все имена, повторно вводимые или вводимые объявлением, связываются с ним в его целевой области видимости.

Сущность принадлежит области видимости S, если S является целевой областью видимости объявления этой сущности.

//                global  scope  scope
//                scope     S      T
int x;         //   ─┐                 // program point X
               //    │
{              //    │     ─┐
    {          //    │      │     ─┐
        int y; //    │      │      │   // program point Y
    }          //    │      │     ─┘
}              //   ─┘     ─┘

В приведённой выше программе:

  • Глобальная область видимости, область видимости S и область видимости T содержат точку программы Y.
    • Другими словами, все эти три области видимости являются внешними в точке программы Y.
  • Глобальная область видимости содержит области видимости S и T, а область видимости S содержит область видимости T.
  • Следовательно, область видимости T является наименьшей из всех трёх, а это значит, что:
    • Область видимости T является непосредственной в точке программы Y.
    • Объявление переменной y принадлежит области видимости T в точке своего положения.
    • Область видимости T является целевой областью видимости объявления y.
    • Переменная y принадлежит области видимости T.
  • Область видимости S является родительской для области видимости T, а глобальная область видимости — родительской для области видимости S.
  • Область видимости S расположена между точкой программы X и областью видимости T.

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

Каждый

  • оператор выбора (if, switch),
  • оператор цикла (for, цикл range-for(начиная с C++11), while, do-while),
  • оператор раскрытия (templatefor),
(начиная с C++26)
  • обработчик или
  • составной оператор, который не является compound-statement обработчика,

вводит область видимости блока, включающую этот оператор или обработчик.

Переменная, принадлежащая области видимости блока, называется переменной блока .

int i = 42;
int a[10];

for (int i = 0; i < 10; i++) // inner “i” inhabits the block scope
    a[i] = i;                // introduced by the for-statement

int j = i; // j = 42

Если объявление принадлежит области видимости блока S и объявляет функцию либо использует спецификатор extern, объявление не должно быть связано с именованным модулем (начиная с C++20), его целевой областью видимости является более внешняя область видимости (самая внутренняя внешняя область видимости пространства имён), но имя связывается с ним в непосредственной области видимости S.

Если объявление, которое не является объявлением с независимым именем и(начиная с C++26), связывающее имя в области видимости блока S, принадлежит

  • compound-statement тела функции или блока try функции,
  • составному оператору { body } лямбда-выражения,
(начиная с C++11)
  • подоператору оператора выбора или цикла, который сам не является оператором выбора или цикла, либо
  • обработчику блока try функции,

и потенциально конфликтует с объявлением, целевой областью видимости которого является родительская область видимости S, программа некорректна.

if (int x = f())  // declares “x”
{ // the if-block is a substatement of the if-statement
    int x;        // error: redeclaration of “x”
}
else
{ // the else-block is also a substatement of the if-statement
    int x;        // error: redeclaration of “x”
}

void g(int i)
{
    extern int i; // error: redeclaration of “i”
}

Область видимости параметров функции

Каждое объявление параметра P вводит область видимости параметров функции, включающую P.

  • Если объявляемый параметр находится в списке параметров объявления функции:
    • Если объявление функции является определением функции, введённая область видимости продолжается до конца определения функции.
    • Иначе (если объявление функции является прототипом функции) введённая область видимости продолжается до конца декларатора функции.
    • В обоих случаях область видимости не включает точку положения объявления функции.
  • Если объявляемый параметр находится в списке параметров лямбда-выражения, введённая область видимости продолжается до конца { body }.
(начиная с C++11)
  • Если объявляемый параметр находится в списке параметров направляющей вывода, введённая область видимости продолжается до конца этой направляющей вывода.
(начиная с C++17)
  • Если объявляемый параметр находится в списке параметров выражения requires, введённая область видимости продолжается до конца { requirement-seq }.
(начиная с C++20)
int f(int n) // the declaration of the parameter “n”
{            // introduces a function parameter scope
    /* ... */
}            // the function parameter scope ends here

Область видимости лямбда-выражения

Каждое лямбда-выражение вводит область видимости лямбда-выражения, которая начинается сразу после [captures ] и продолжается до конца { body }.

Захваты лямбда-выражения с инициализаторами E принадлежат области видимости лямбда-выражения, введённой E.

auto lambda = [x = 1, y]() // this lambda expression introduces a lambda scope,
{                          // it is the target scope of capture “x”
    /* ... */
};                         // the lambda scope ends before the semicolon
(начиная с C++14)

Область видимости пространства имён

Каждое определение пространства имён для пространства имён N вводит область видимости пространства имён S, включающую объявления всех определений пространства имён для N.

Для каждого повторного объявления или специализации, не являющихся дружественными, целевая область видимости которых — S или содержится в S, следующие фрагменты также входят в область видимости S:

  • Для повторного объявления класса (шаблона) или специализации шаблона класса — фрагмент после его class-head-name.
  • Для повторного объявления перечисления — фрагмент после его enum-head-name.
  • Для любого другого повторного объявления или специализации — фрагмент после unqualified-id или qualified-id декларатора.

Глобальная область видимости является областью видимости пространства имён глобального пространства имён.

namespace V   // the namespace definition of “V”
{             // introduces a namespace scope “S”
    // the first part of scope “S” begins here
    void f();
    // the first part of scope “S” ends here
}

void V::f()   // the portion after “f” is also a part of scope “S”
{
    void h(); // declares V::h
}             // the second part of scope “S” ends here

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

Каждое объявление класса или шаблона класса C вводит область видимости класса S, включающую member-specification определения класса C.

Для каждого повторного объявления или специализации, не являющихся дружественными, целевая область видимости которых — S или содержится в S, следующие фрагменты также входят в область видимости S:

  • Для повторного объявления класса (шаблона) или специализации шаблона класса — фрагмент после его class-head-name.
  • Для повторного объявления перечисления — фрагмент после его enum-head-name.
  • Для любого другого повторного объявления или специализации — фрагмент после unqualified-id или qualified-id декларатора.
class C       // the class definition of “C”
{             // introduces a class scope “S”
    // the first part of scope “S” begins here
    void f();
    // the first part of scope “S” ends here
}

void C::f()   // the portion after “f” is also a part of scope “S”
{
    /* ... */
}             // the second part of scope “S” ends here

Область видимости перечисления

Каждое объявление перечисления E вводит область видимости перечисления, включающую enumerator-list объявления перечисления E (если оно есть) без базового типа(начиная с C++11).

enum class E // the enumeration declaration of “E”
{            // introduces an enumeration scope “S”
    // scope “S” begins here
    e1, e2, e3
    // scope “S” ends here
}

Область видимости параметров шаблона

Каждый параметр-шаблон шаблона вводит область видимости параметров шаблона, включающую весь список параметров шаблона и предложения require(начиная с C++20) этого параметра-шаблона шаблона.

Каждое объявление шаблона D вводит область видимости параметров шаблона S, простирающуюся от начала списка параметров шаблона D до конца D. Любое объявление за пределами списка параметров шаблона, которое принадлежало бы S, вместо этого принадлежит той же области видимости, что и D.

Области видимости параметров шаблона принадлежат только параметры шаблона, и только у областей видимости параметров шаблона родительской областью видимости может быть другая область видимости параметров шаблона.

// the class template declaration of “X”
// introduces a template parameter scope “S1”
template
<
    // scope “S1” begins here
    template // the template template parameter “T”
             // introduces another template parameter scope “S2”
    <
        typename T1
        typename T2
    > requires std::convertible_from<T1, T2> // scope “S2” ends here
    class T,
    typename U
>
class X; // scope “S1” ends before the semicolon

namespace N
{
    template <typename T>
    using A = struct X; // “X” inhabits the same scope as template
                        // declaration, namely the scope of “N”
}

Область видимости утверждения контракта

Каждое утверждение контракта C вводит область видимости утверждения контракта, включающую C.

Если утверждение постусловия содержит identifier, не являющийся независимым от имени, и утверждение постусловия связано с функцией func, оно потенциально конфликтует с объявлением D, целевая область видимости которого является одной из следующих областей видимости; в таком случае программа некорректна:

  • Область видимости параметров функции func.
  • Если D связано с лямбда-выражением, ближайшая внешняя область видимости лямбда-выражения для утверждения предусловия.
(начиная с C++26)

Точка объявления

Как правило, имя становится видимым после точки положения его первого объявления, которая определяется следующим образом.

Точка положения имени, объявленного в простом объявлении, находится сразу после декларатора этого имени и перед его инициализатором, если он есть.

int x = 32; // outer x is in scope

{
    int x = x; // inner x is in scope before the initializer (= x)
               // this does not initialize inner x with the value of outer x (32),
               // this initializes inner 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 in the lambda and can
// be correctly captured by reference, giving a recursive function
const int x = 2; // outer x is in scope

{
    int x[x] = {}; // inner x is in scope before the initializer (= {}),
                   // but after the declarator (x[x])
                   // in the declarator, outer x is still in scope
                   // this declares an array of 2 int
}

Точка положения объявления класса или шаблона класса находится сразу после идентификатора, задающего имя класса (или идентификатора шаблона, задающего имя специализации шаблона), в его заголовке класса. Имя класса или шаблона класса уже находится в области видимости в списке базовых классов.

struct S: std::enable_shared_from_this<S> {}; // S is in scope at the colon

Точка положения спецификатора enum или непрозрачного объявления перечисления(начиная с C++11) находится сразу после идентификатора, задающего имя перечисления.

enum E : int // E is in scope at the colon
{
    A = sizeof(E)
};

Точка положения объявления псевдонима типа или шаблона псевдонима находится сразу после идентификатора типа, на который ссылается псевдоним.

using T = int; // outer T is in scope at the semicolon

{
    using T = T*; // inner T is in scope at the semicolon,
                  // outer T is still in scope before the semicolon
                  // same as 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 is in scope at the comma
          Base<next>::value; // Derived::value is 1
};

Точка положения перечислителя находится сразу после его определения (в отличие от переменных, не перед инициализатором).

const int x = 12;

{
    enum
    {
        x = x + 1, // enumerator x is in scope at the comma,
                   // outer x is in scope before the comma,
                   // enumerator x is initialized to 13
        y = x + 1  // 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>
};

Точка положения неявного объявления заранее определённой переменной __func__ с локальной областью видимости функции находится непосредственно перед телом определения функции.

(начиная с C++11)

Точка положения объявления структурированной привязки находится сразу после identifier-list, однако инициализаторам структурированной привязки запрещено ссылаться на любое из объявляемых имён.

(начиная с C++17)

Точка положения переменной или структурированных привязок(начиная с C++17), объявленных в item-declaration цикла range-for, находится сразу после range-initializer.

Точка положения переменной или структурированных привязок, объявленных в item-declaration раскрытия templatefor, находится сразу после expansion-initializer.

(начиная с C++26)
std::vector<int> x;

for (auto x : x) // vector x is in scope before the closing parenthesis,
                 // auto x is in scope at the closing parenthesis
{
    // the auto x is in scope
}
(начиная с C++11)

Точка положения параметра шаблона находится сразу после его полного объявления параметра шаблона (включая необязательный аргумент по умолчанию).

typedef unsigned char T;

template<
    class T = T, // template parameter T is in scope at the comma,
                 // typedef name of unsigned char is in scope before the comma
    T // template parameter T is in scope
    N = 0
>
struct A
{
};

Точка положения утверждения постусловия с identifier находится сразу после его :.

(начиная с C++26)

Точка положения определения концепта находится сразу после имени концепта, однако определениям концептов запрещено ссылаться на объявляемое имя концепта.

(начиная с C++20)

Точка положения именованного определения пространства имён находится сразу после имени пространства имён.

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были ретроспективно применены к ранее опубликованным стандартам C++.

DR Применяется к Поведение согласно опубликованной версии Корректное поведение
CWG 2793 C++98 объявление extern в области видимости блока могло
конфликтовать с другим объявлением в родительской области видимости
запрещено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
    • 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 для области видимости

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/scope

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API