6 Basic concepts [basic]

Note: этом разделе представлены основные концепции языка C ++. Он объясняет разницу между объектом и именем и их отношение к категориям значений для выражений. В нем представлены концепции объявления и определения и представлены понятия C ++ о типе, области действия, связи и продолжительности хранения. Обсуждаются механизмы запуска и завершения программы. Наконец, в этом разделе представлены основные типы языка и перечислены способы построения из них составных типов. ]end note

[ Note: Этот пункт не распространяется на концепции, влияющие только на одну часть языка. Такие концепции обсуждаются в соответствующих разделах. ] end note

An entity - значение, объект, ссылка, функция, перечислитель, тип, член класса, битовое поле, шаблон, специализация шаблона, пространство имен или пакет параметров.

name Является использование identifier, operator-function-id, literal-operator-id, conversion-function-id, или , template-idчто обозначает объект или метку ([stmt.goto], [stmt.label]).

Каждое имя, обозначающее объект, вводится знаком declaration. Каждое имя, обозначающее метку, вводится либо через a, goto statement либо через a labeled-statement.

A variable вводится объявлением ссылки, отличной от нестатического члена данных или объекта. Имя переменной, если есть, обозначает ссылку или объект.

Некоторые имена обозначают типы или шаблоны. В общем, всякий раз, когда встречается имя, необходимо определить, обозначает ли это имя одну из этих сущностей, прежде чем продолжить синтаксический анализ программы, которая его содержит. Процесс, определяющий это, называется name lookup ([basic.lookup]).

Два имени - это the same если

  • они identifiers состоят из одной и той же последовательности символов, или

  • они operator-function-ids формируются одним и тем же оператором, или

  • они conversion-function-ids сформированы одного типа, или

  • они template-ids относятся к одному и тому же классу, функции или переменной ([temp.type]), или

  • они представляют собой имена, literal operators образованные одним и тем же буквальным суффиксным идентификатором.

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

6.1 Declarations and definitions [basic.def]

A declaration может ввести одно или несколько имен в единицу перевода или повторно объявить имена, введенные предыдущими объявлениями. Если это так, в объявлении указываются интерпретация и атрибуты этих имен. Объявление также может иметь следующие последствия:

Декларация - это definition если

[ Example: Все, кроме одного из следующих определений:

int a;                          // defines a
extern const int c = 1;         // defines c
int f(int x) { return x+a; }    // defines f and defines x
struct S { int a; int b; };     // defines S, S​::​a, and S​::​b
struct X {                      // defines X
  int x;                        // defines non-static data member x
  static int y;                 // declares static data member y
  X(): x(0) { }                 // defines a constructor of X
};
int X::y = 1;                   // defines X​::​y
enum { up, down };              // defines up and down
namespace N { int d; }          // defines N and N​::​d
namespace N1 = N;               // defines N1
X anX;                          // defines anX

тогда как это просто объявления:

extern int a;                   // declares a
extern const int c;             // declares c
int f(int);                     // declares f
struct S;                       // declares S
typedef int Int;                // declares Int
extern X anotherX;              // declares anotherX
using N::d;                     // declares d

end example]

[ В некоторых случаях реализации C ++ неявно определяют , , , , , или функции - члены. ] [ УчитываяNote: default constructorcopy constructormove constructorcopy assignment operatormove assignment operator destructor end noteExample:

#include <string>

struct C {
  std::string s;    // std​::​string is the standard library class (Clause [strings])
};

int main() {
  C a;
  C b = a;
  b = a;
}

реализация будет неявно определять функции, чтобы сделать определение C эквивалентным

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
    // : s(std​::​move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
    // { s = std​::​move(x.s); return *this; }
  ~C() { }
};

end example]

[ Note: Имя класса также может быть неявно объявлено с помощью elaborated-type-specifier. ]end note

Программа плохо сформирована, если определение любого объекта дает объекту значение incomplete type.

Появившись внутри приготовился корпусе declaration-seqв linkage-specificationне влияет на заявление , является ли определение.

6.2 One-definition rule [basic.def.odr]

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

Выражение is, potentially evaluated если оно не является его unevaluated operand частным выражением. Набор potential results выражения e определяется следующим образом:

  • Если e есть id-expression, набор содержит только e.

  • Если e - subscripting operation с операндом-массивом, набор содержит потенциальные результаты этого операнда.

  • Если e - class member access выражение, набор содержит потенциальные результаты объектного выражения.

  • Если e - pointer-to-member выражение, второй операнд которого является постоянным выражением, набор содержит потенциальные результаты объектного выражения.

  • Если e имеет форму (e1), набор содержит потенциальные результаты e1.

  • Если e - conditional выражение glvalue , набор представляет собой объединение наборов потенциальных результатов второго и третьего операндов.

  • Если e - a comma expression, набор содержит потенциальные результаты правого операнда.

  • В противном случае набор пуст.

[ Note: Этот набор является (возможно, пустым) набором id-expressions, каждое из которых является либо e частью выражения e. [ Example: В следующем примере набор потенциальных результатов инициализатора n содержит первое S​::​x подвыражение, но не второе S​::​x подвыражение.

struct S { static const int x = 0; };
const int &f(const int &r);
int n = b ? (1, S::x)  // S​::​x is not odr-used here
          : f(S::x);   // S​::​x is odr-used here, so a definition is required

end example] ]end note

Переменная x , имя которой отображается как потенциально оцениваемое выражение, ex - это odr-used , ex если только применение lvalue-to-rvalue conversion к не x дает constant expression , который не вызывает никаких нетривиальных функций, и, если x является объектом, ex является элементом набора потенциальных результатов выражения e, где либо lvalue-to-rvalue conversion применяется к e, или e это discarded-value expression. this используется odr, если оно появляется как потенциально оцениваемое выражение (в том числе как результат неявного преобразования в теле a non-static member function). Виртуальная функция-член используется odr, если она не чистая. Функция , чье имя появляется как потенциально-оценивали выражение ODR-используется , если он является единственным результатом поиска или выбранный элемент из набора перегруженных функций ([basic.lookup], [over.match], [over.over]), если это не является чистой виртуальной функции и либо его имя не явно квалифицировано, или выражение образует указатель на member ([expr.unary.op]). [ Note: Это касается calls to named functions, operator overloading, user-defined conversions, функции распределения для размещения new-expressions, а также инициализация не по умолчанию ([dcl.init]). Конструктор, выбранный для копирования или перемещения объекта типа класса, используется odr, даже если вызов фактически отменен реализацией ([class.copy]). ] Функция выделения или освобождения для класса используется odr, появляясь в потенциально оцениваемом выражении, как указано в и . Функция освобождения класса используется odr выражением удаления, появляющимся в потенциально оцениваемом выражении, как указано в и . Функция выделения или освобождения без размещения для класса используется odr определением конструктора этого класса. Функция освобождения без размещения для класса используется odr определением деструктора этого класса или выбирается поиском в точке определения виртуального деструктора ( ). Функция оператора присваивания в классе используется odr неявно определенной функцией присваивания копии или присваивания перемещения для другого класса, как указано в . Конструктор для класса используется odr, как указано в . Деструктор для класса используется odr, если это так . end notenew-expression [expr.new] [class.free] [expr.delete] [class.free][class.dtor]27 [class.copy] [dcl.init]potentially invoked

Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется odr в этой программе вне a discarded statement; диагностика не требуется. Определение может появиться в программе явно, его можно найти в стандартной или пользовательской библиотеке или (при необходимости) оно определено неявно (см. [class.ctor], [class.dtor] И [class.copy]). Встроенная функция или переменная должна быть определена в каждой единице трансляции, в которой она используется odr вне отвергнутого оператора.

В единице перевода требуется ровно одно определение класса, если класс используется таким образом, который требует, чтобы тип класса был полным. [ Example: Следующая полная единица перевода составлена ​​правильно, хотя в ней никогда не определяется X:

struct X;                       // declare X as a struct type
struct X* x1;                   // use X in pointer formation
X* x2;                          // use X in pointer formation

end example] [ Note: Правила для объявлений и выражений описывают, в каких контекстах требуются полные типы классов. Тип класса T должен быть полным, если:

end note]

Там может быть более одного определения class type, enumeration type, инлайн функции с внешним связыванием ([dcl.inline]), инлайн переменной с внешним связыванием ([dcl.inline]), class template, нестатический function template, static data member of a class template, member function of a class templateили шаблон специализации , для которых некоторые параметры шаблона не указаны ([temp.spec], [temp.class.spec]) в программа при условии, что каждое определение появляется в разных единицах перевода, и при условии, что определения удовлетворяют следующим требованиям. Если такая сущность D определена в нескольких единицах перевода, то

  • каждое определение D должно состоять из одной и той же последовательности токенов; а также

  • в каждом определении Dсоответствующие имена, [basic.lookup]выполняемые в соответствии с поиском, должны относиться к объекту, определенному в определении D, или должны относиться к тому же объекту после overload resolution и после сопоставления частичной специализации шаблона ([temp.over]), за исключением того, что имя может ссылаться на

    • энергонезависимый const объект с внутренней связью или без нее, если объект

      • имеет один и тот же буквальный тип во всех определениях D,

      • инициализируется с constant expression,

      • не используется odr ни в одном определении D, и

      • имеет одинаковое значение во всех определениях D,

      или

    • ссылка с внутренней связью или без нее, инициализированная константным выражением, так что ссылка ссылается на один и тот же объект во всех определениях D;

    а также

  • в каждом определении Dсоответствующие объекты должны иметь одинаковую языковую связь; а также

  • в каждом определении Dупомянутых перегруженных операторов, неявных вызовов функций преобразования, конструкторов, функций оператора new и функций удаления оператора, должны ссылаться на одну и ту же функцию или на функцию, определенную в определении D; а также

  • в каждом определении Dаргумент по умолчанию, используемый (неявным или явным) вызовом функции, обрабатывается так, как если бы его последовательность лексем присутствовала в определении D; то есть аргумент по умолчанию подчиняется требованиям, описанным в этом параграфе (и, если аргумент по умолчанию имеет подвыражения с аргументами по умолчанию, это требование применяется рекурсивно).28

  • if D является классом с неявно объявленным constructor, это как если бы конструктор был неявно определен в каждой единице перевода, где он используется odr, и неявное определение в каждой единице перевода должно вызывать один и тот же конструктор для подобъекта D. [Example:

    // translation unit 1:
    struct X {
      X(int, int);
      X(int, int, int);
    };
    X::X(int, int = 0) { }
    class D {
      X x = 0;
    };
    D d1;                           // X(int, int) called by D()
    
    // translation unit 2:
    struct X {
      X(int, int);
      X(int, int, int);
    };
    X::X(int, int = 0, int = 0) { }
    class D {
      X x = 0;
    };
    D d2;                           // X(int, int, int) called by D();
                                    // D()'s implicit definition violates the ODR
    

    end example]

Если D является шаблоном и определен в нескольких единицах перевода, то предыдущие требования должны применяться как к именам из охватывающей области действия шаблона, используемой в определении шаблона ([temp.nondep]), так и к зависимым именам в точке создания экземпляра ([temp.dep]). Если определения D удовлетворяют всем этим требованиям, то поведение будет таким, как если бы было одно определение D. Если определения D не удовлетворяют этим требованиям, поведение не определено.

Реализация не требуется для вызова функций выделения и освобождения из конструкторов или деструкторов; однако это допустимый метод реализации.

[dcl.fct.default] описывает, как ищутся имена аргументов по умолчанию.

6.3 Scope [basic.scope]

6.3.1 Declarative regions and scopes [basic.scope.declarative]

Каждое имя вводится в некоторой части текста программы, называемой a , которая является самой большой частью программы, в которой это имя , то есть в которой это имя может использоваться как неквалифицированное имя для ссылки на один и тот же объект. В общем, каждое конкретное имя допустимо только в пределах некоторой, возможно, несмежной части текста программы, называемой его . Чтобы определить объем объявления, иногда удобно обратиться к объекту объявления. Область действия объявления такая же, как и ее потенциальная область, если только потенциальная область не содержит другое объявление с тем же именем. В этом случае потенциальная область действия объявления во внутренней (содержащейся) декларативной области исключается из области действия объявления во внешней (содержащей) декларативной области.declarative region valid scopepotential scope

[ Example: В

int j = 24;
int main() {
  int i = j, j;
  j = 42;
}

идентификатор j объявляется дважды как имя (и используется дважды). Декларативная область первого j включает весь пример. Потенциальная область действия первого j начинается сразу после этого j и продолжается до конца программы, но ее (фактическая) область действия исключает текст между символами , и }. Декларативная область второго объявления j ( j непосредственно перед точкой с запятой) включает весь текст между { и }, но его потенциальная область исключает объявление i. Область действия второго объявления j совпадает с его потенциальной областью. ]end example

Имена, объявленные объявлением, вводятся в область видимости, в которой происходит объявление, за исключением того, что наличие a friend specifier, некоторые виды использования elaborated-type-specifierи using-directives изменяют это общее поведение.

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

  • все они должны относиться к одному и тому же объекту или все относиться к функциям и шаблонам функций; или

  • ровно одно объявление должно объявлять имя класса или имя перечисления, которое не является именем typedef, а все другие объявления должны ссылаться на одну и ту же переменную, нестатический член данных или перечислитель, или все они относятся к функциям и шаблонам функций; в этом случае имя класса или имя перечисления hidden. [ Note: Имя пространства имен или имя шаблона класса должно быть уникальным в своей декларативной области ([namespace.alias], пункт [temp]). ] end note

[ Note: Эти ограничения применяются к декларативной области, в которой вводится имя, которая не обязательно совпадает с областью, в которой происходит объявление. В частности, elaborated-type-specifiers и friend declarations может ввести (возможно, невидимое) имя во включающее пространство имен; эти ограничения распространяются на этот регион. Локальные объявления extern ([basic.link]) могут вводить имя в декларативную область, где появляется объявление, а также вводить (возможно, не видимое) имя во включающее пространство имен; эти ограничения распространяются на оба региона. ] end note

[ Note: Правила поиска имени кратко изложены в [basic.lookup]. ]end note

6.3.2 Point of declaration [basic.scope.pdecl]

Для point of declaration имени ставится сразу после его завершения declarator и перед initializer(если есть), за исключением случаев, указанных ниже. [Example:

unsigned char x = 12;
{ unsigned char x = x; }

Здесь второй x инициализируется своим (неопределенным) значением. ] end example

[ Имя из внешней области видимости остается видимым до момента объявления имени, которое его скрывает. [Note: Example:

const int  i = 2;
{ int  i[i]; }

объявляет массив из двух целых чисел в области видимости блока. ] ] end example end note

Точка объявления для класса или шаблона класса, сначала объявленного a, class-specifierнаходится сразу после identifierили simple-template-id(если есть) в его class-head. Точка объявления для перечисления находится сразу после identifier(если есть) либо в его, enum-specifierлибо в его первом opaque-enum-declaration, в зависимости от того, что наступит раньше. Точка объявления псевдонима или шаблона псевдонима следует сразу за тем, type-idна который ссылается псевдоним.

Точка объявления using-declarator, которая не называет конструктор, находится сразу после using-declarator.

Точка объявления перечислителя находится сразу после его enumerator-definition. [Example:

const int x = 12;
{ enum { x = x }; }

Здесь перечислитель x инициализируется значением константы x, а именно 12. ] end example

После точки объявления члена класса имя члена можно искать в области его класса. [ Это верно, даже если класс является неполным. Например,Note:

struct X {
  enum E { z = 16 };
  int b[X::z];      // OK
};

end note]

Точка объявления класса, впервые объявленного в файле, elaborated-type-specifierвыглядит следующим образом:

Точка объявления для an injected-class-name следует сразу за открывающей скобкой определения класса.

Точка объявления для локальной предопределенной переменной ([dcl.fct.def]) находится непосредственно перед function-bodyопределением функции.

Точка объявления параметра шаблона - сразу после его завершения template-parameter. [Example:

typedef unsigned char T;
template<class T
  = T     // lookup finds the typedef name of unsigned char
  , T     // lookup finds the template parameter
    N = 0> struct A { };

end example]

[ Note: Дружественные объявления относятся к функциям или классам, которые являются членами ближайшего включающего пространства имен, но они не вводят новые имена в это пространство имен ([namespace.memdef]). Объявления функций в области видимости блока и объявления переменных со extern спецификатором в области видимости блока относятся к объявлениям, которые являются членами включающего пространства имен, но они не вводят новые имена в эту область. ]end note

[ Note: Для создания экземпляра шаблона см [temp.point].. ]end note

6.3.3 Block scope [basic.scope.block]

Имя, объявленное в a, block является локальным для этого блока; это есть block scope. Его потенциальная область действия начинается с его point of declaration и заканчивается в конце его блока. Переменная, объявленная в области видимости блока, - это local variable.

Потенциальная область видимости имени параметра функции (включая имя, появляющееся в a lambda-declarator) или предопределенной переменной функции в a function definition начинается с точки ее объявления. Если функция имеет function-try-blockпотенциальную область видимости параметра или предопределенной переменной, локальной для функции, заканчивается в конце последнего связанного обработчика, в противном случае она заканчивается в конце самого внешнего блока определения функции. Имя параметра не должно повторно объявляться ни во внешнем блоке определения функции, ни во внешнем блоке любого обработчика, связанного с a function-try-block.

Имя, объявленное в объекте, exception-declaration является локальным по отношению к handlerобъекту, и его нельзя повторно объявлять во внешнем блоке handler.

Имена объявлены в init-statement, то for-range-declarationи в conditionиз if, while, for, и switch заявления являются локальными к if, while, forили switch заявление ( в том числе и контролируемое заявление), и не может быть повторно объявлен в последующем состоянии этого заявления , ни в наружном блоке ( или, для if оператора, любой из самых внешних блоков) контролируемого оператора; см [stmt.select].

6.3.4 Function prototype scope [basic.scope.proto]

В объявлении функции или в любом деклараторе функции, кроме декларатора a function definition, имена параметров (если предоставлены) имеют область прототипа функции, которая заканчивается в конце ближайшего включающего декларатора функции.

6.3.5 Function scope [basic.funscope]

Labels имеют function scope и могут использоваться в любом месте функции, в которой они объявлены. Только ярлыки имеют область действия.

6.3.6 Namespace scope [basic.scope.namespace]

Декларативная область - namespace-definitionэто его namespace-body. Объекты, объявленные в a, namespace-bodyназываются members принадлежащими пространству имен, а имена, введенные этими объявлениями в декларативную область пространства имен, называются member names принадлежащими пространству имен. Имя члена пространства имен имеет область пространства имен. Его потенциальная область действия включает пространство имен, начиная с имени point of declaration ; и для каждого, using-directiveкоторый назначает пространство имен члена, потенциальная область действия члена включает ту часть потенциальной области видимости, using-directiveкоторая следует за точкой объявления члена. [Example:

namespace N {
  int i;
  int g(int a) { return a; }
  int j();
  void q();
}
namespace { int l=1; }
// the potential scope of l is from its point of declaration to the end of the translation unit

namespace N {
  int g(char a) {   // overloads N​::​g(int)
    return l+a;     // l is from unnamed namespace
  }

  int i;            // error: duplicate definition
  int j();          // OK: duplicate function declaration

  int j() {         // OK: definition of N​::​j()
    return g(i);    // calls N​::​g(int)
  }
  int q();          // error: different return type
}

end example]

На член пространства имен также можно ссылаться после того, как ​::​ оператор разрешения области видимости ([expr.prim]) применен к имени его пространства имен или имени пространства имен, которое назначает пространство имен члена в using-directive; см [namespace.qual].

Самая внешняя декларативная область единицы перевода - это также пространство имен, называемое global namespace. Имя, объявленное в глобальном пространстве имен, имеет global namespace scope (также называется global scope). Потенциальная область видимости такого имени начинается point of declaration и заканчивается в конце единицы трансляции, которая является ее декларативной областью. Имя с глобальной областью пространства имен называется global name.

6.3.7 Class scope [basic.scope.class]

Потенциальная область видимости имени, объявленного в классе, состоит не только из декларативной области, следующей за точкой объявления имени, но также из всех тел функций, аргументов по умолчанию noexcept-specifiersи brace-or-equal-initializers нестатических элементов данных в этом классе (включая такие вещи в вложенные классы).

Имя, N используемое в классе, S должно ссылаться на одно и то же объявление в его контексте и при повторной оценке в завершенной области S. Нарушение этого правила не требует диагностики.

Имя, объявленное в функции-члене, скрывает объявление с тем же именем, область действия которого простирается до конца класса функции-члена или за его пределами.

Потенциальная область действия объявления, которая простирается до конца определения класса или за его пределами, также распространяется на области, определенные определениями его членов, даже если члены определены лексически вне класса (это включает определения статических элементов данных, определения вложенных классов, и определения функций-членов, включая тело функции-члена и любую часть декларативной части таких определений, которая следует за declarator-id, включая a parameter-declaration-clauseи any default arguments).

[Example:

typedef int  c;
enum { i = 1 };

class X {
  char  v[i];                       // error: i refers to ​::​i but when reevaluated is X​::​i
  int  f() { return sizeof(c); }    // OK: X​::​c
  char  c;
  enum { i = 2 };
};

typedef char*  T;
struct Y {
  T  a;                             // error: T refers to ​::​T but when reevaluated is Y​::​T
  typedef long  T;
  T  b;
};

typedef int I;
class D {
  typedef I I;                      // error, even though no reordering involved
};

end example]

Имя члена класса должно использоваться только следующим образом:

  • в рамках своего класса (как описано выше) или класса derived из своего класса,

  • после . применения оператора к выражению типа его класса ([expr.ref]) или класса, производного от его класса,

  • после -> применения оператора к указателю на объект его класса ([expr.ref]) или класса, производного от его класса,

  • после того, как ​::​ оператор разрешения области видимости ([expr.prim]) применен к имени его класса или класса, производного от его класса.

6.3.8 Enumeration scope [basic.scope.enum]

Имя scoped enumerator имеет enumeration scope. Его потенциальная область действия начинается в точке объявления и заканчивается в конце enum-specifier.

6.3.9 Template parameter scope [basic.scope.temp]

Декларативная область имени параметра шаблона шаблона template-parameter- наименьшая, template-parameter-list в которой имя было введено.

Декларативная область имени параметра шаблона шаблона - наименьшая, template-declarationв которой имя было введено. К этой декларативной области принадлежат только имена параметров шаблона; любой другой вид имени, введенный с помощью declarationa template-declaration, вместо этого вводится в ту же декларативную область, где он был бы введен в результате нешаблонного объявления с тем же именем. [Example:

namespace N {
  template<class T> struct A { };               // #1
  template<class U> void f(U) { }               // #2
  struct B {
    template<class V> friend int g(struct C*);  // #3
  };
}

Декларативная область T, U и V являются template-declarations по линиям # 1, # 2 и # 3, соответственно. Но имена A, f, g и C все они принадлежат к одной и той же декларативной области , а именно - namespace-bodyиз N. (g по-прежнему считается принадлежащей этой декларативной области, несмотря на то, что она была скрыта во время поиска квалифицированного и неквалифицированного имени.) ]end example

Потенциальная область действия имени параметра шаблона начинается с его point of declaration и заканчивается в конце его декларативной области. [ Note: Это означает, что a template-parameterможет использоваться в объявлении последующих template-parameters аргументов и их аргументов по умолчанию, но не может использоваться в предшествующих template-parameters аргументах или их аргументах по умолчанию. Например,

template<class T, T* p, class U = T> class X { /* ... */ };
template<class T> void f(T* p = new T);

Это также означает, что a template-parameterможет использоваться в спецификации базовых классов. Например,

template<class T> class X : public Array<T> { /* ... */ };
template<class T> class Y : public T { /* ... */ };

Использование параметра шаблона в качестве базового класса подразумевает, что класс, используемый в качестве аргумента шаблона, должен быть определен, а не просто объявлен при создании экземпляра шаблона класса. ]end note

Декларативная область имени параметра шаблона вложена в непосредственно включающую декларативную область. [ Note: В результате template-parameter hides любой объект с тем же именем во включающей области. [Example:

typedef int N;
template<N X, typename N, template<N Y> class T> struct A;

Здесь, X является параметром шаблона не-типа типа int и Y является параметром шаблона не-типа того же типа в качестве второго параметра шаблона из A. ] ] end example end note

[ Note: Поскольку имя параметра шаблона не может быть повторно объявлено в пределах его потенциальной области ([temp.local]), область действия параметра шаблона часто является его потенциальной областью. Однако имя параметра шаблона все еще можно скрыть; см [temp.local]. ] end note

6.3.10 Name hiding [basic.scope.hiding]

Имя может быть скрыто явным объявлением того же имени во вложенной декларативной области или производном классе ([class.member.lookup]).

class name Или enumeration name могут быть скрыты по имени переменной, члена данных, функции или счетчику , объявленного в том же объеме. Если имя класса или перечисления и переменная, член данных, функция или перечислитель объявлены в одной и той же области (в любом порядке) с одним и тем же именем, имя класса или перечисления скрыто везде, где переменная, член данных, функция или имя перечислителя видно.

В определении функции-члена объявление имени в области видимости блока скрывает объявление члена класса с тем же именем; см [basic.scope.class]. Объявление члена в a derived class скрывает объявление члена одноименного базового класса; см [class.member.lookup].

Во время поиска имени, уточненного именем пространства имен, объявления, которые в противном случае стали бы видимыми с помощью a, using-directiveмогут быть скрыты объявлениями с тем же именем в пространстве имен, содержащем using-directive; см [namespace.qual].

Если имя находится в области видимости и не скрыто, оно считается скрытым visible.

6.4 Name lookup [basic.lookup]

Правила поиска имен применяются единообразно ко всем именам (включая typedef-names, namespace-names ([basic.namespace]) и class-names ([class.name])) везде, где грамматика допускает такие имена в контексте, обсуждаемом определенным правилом. Поиск имени связывает использование имени с набором объявлений ([basic.def]) этого имени. Объявления, найденные с помощью поиска по имени, должны либо все объявлять один и тот же объект, либо все должны объявлять функции; в последнем случае говорят, что объявления образуют набор перегруженных функций ([over.load]). Overload resolution происходит после успешного поиска имени. access rules Считаются только один раз имя поиска и разрешения перегрузки функции (если применимо) удалось. Только после поиска имени, разрешения перегрузки функции (если применимо) и проверки доступа атрибуты, введенные объявлением имени, используются далее при обработке выражения (пункт [expr]).

Имя, «найденное в контексте выражения», ищется как неквалифицированное имя в области, в которой найдено выражение.

injected-class-name Класса также считается членом этого класса в целях сокрытия имен и поиска.

[ Note: [basic.link] обсуждает вопросы связи. Понятия области видимости, точки объявления и сокрытия имени обсуждаются в [basic.scope]. ] end note

6.4.1 Unqualified name lookup [basic.lookup.unqual]

Во всех случаях, перечисленных в [basic.lookup.unqual], области действия ищутся для объявления в порядке, указанном в каждой из соответствующих категорий; Поиск имени заканчивается, как только для имени найдено объявление. Если объявление не найдено, программа имеет неправильный формат.

Объявления из пространства имен, обозначенного символом a, using-directiveстановятся видимыми в пространстве имен, содержащем using-directive; см [namespace.udir]. Для целей правил поиска неквалифицированных имен, описанных в [basic.lookup.unqual], объявления из пространства имен, назначенного using-directiveэлементом, считаются членами этого включающего пространства имен.

Поиск неполного имени, используемого в postfix-expressionкачестве вызова функции, описан в [basic.lookup.argdep]. [ Note: Для определения (во время синтаксического анализа), является ли выражение postfix-expressionдля вызова функции, применяются обычные правила поиска имени. Правила [basic.lookup.argdep] не влияют на синтаксическую интерпретацию выражения. Например,

typedef int f;
namespace N {
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the typedef, not the friend function: equivalent to int(a)
    }
  };
}

Поскольку выражение не является вызовом функции, argument-dependent name lookup не применяется и функция друга f не найдена. ] end note

Имя, используемое в глобальной области видимости, вне какой-либо функции, класса или объявленного пользователем пространства имен, должно быть объявлено до его использования в глобальной области.

Имя, используемое в объявленном пользователем пространстве имен вне определения какой-либо функции или класса, должно быть объявлено до его использования в этом пространстве имен или перед его использованием в пространстве имен, включающем его пространство имен.

В определении функции, которая является членом пространства имен N, имя, используемое после функции, declarator-id29 должно быть объявлено до его использования в блоке, в котором оно используется, или в одном из его включающих блоков ([stmt.block]), или должно быть объявлено перед его использованием в пространство имен N или, если N это вложенное пространство имен, должно быть объявлено перед его использованием в одном из Nвключающих пространств имен. [Example:

namespace A {
  namespace N {
    void f();
  }
}
void A::N::f() {
  i = 5;
  // The following scopes are searched for a declaration of i:
  // 1) outermost block scope of A​::​N​::​f, before the use of i
  // 2) scope of namespace N
  // 3) scope of namespace A
  // 4) global scope, before the definition of A​::​N​::​f
}

end example]

Имя , используемое в определении класса X внешней функции члена тела, аргумент по умолчанию, noexcept-specifier, brace-or-equal-initializerиз нестатического элемента данных или определения вложенного класса ,30 должны быть объявлен в одном из следующих способов:

  • перед его использованием в классе X или быть членом базового класса X ([class.member.lookup]), или

  • if X является a nested class of class Y, до определения X in Y, или должен быть членом базового класса Y (этот поиск применяется, в свою очередь, к Yохватывающим классам, начиная с самого внутреннего включающего класса),31 или

  • если X является local class или является вложенным классом локального класса, перед определением класса X в блоке, включающем определение класса X, или

  • if X является членом пространства имен N, или является вложенным классом класса, который является членом N, или является локальным классом, или вложенным классом в локальном классе функции, которая является членом N, до определения класса X в пространстве имен N или в одном из Nзакрывающих пространств имен.

[Example:

namespace M {
  class B { };
}
namespace N {
  class Y : public M::B {
    class X {
      int a[i];
    };
  };
}

// The following scopes are searched for a declaration of i:
// 1) scope of class N​::​Y​::​X, before the use of i
// 2) scope of class N​::​Y, before the definition of N​::​Y​::​X
// 3) scope of N​::​Y's base class M​::​B
// 4) scope of namespace N, before the definition of N​::​Y
// 5) global scope, before the definition of N

end example] [ Note: При поиске предыдущего объявления класса или функции, введенного friend объявлением, области за пределами самой внутренней области охватывающего пространства имен не рассматриваются; см [namespace.memdef]. ] [ далее описывает ограничения на использование имен в определении класса. далее описывает ограничения на использование имен в определениях вложенных классов. далее описывает ограничения на использование имен в определениях локальных классов. ] end noteNote: [basic.scope.class] [class.nest] [class.local] end note

Для членов класса должно быть объявлено Xимя, используемое в теле функции-члена, в аргументе по умолчанию, в a noexcept-specifier, в brace-or-equal-initializera non-static data memberили в определении члена класса вне определения X, следующее за членом. declarator-id32одним из следующих способов:

  • перед его использованием в блоке, в котором он используется, или во включающем блоке ([stmt.block]), или

  • должен быть членом класса X или быть членом базового класса X ([class.member.lookup]), или

  • if X является nested class классом Y, должен быть членом Yили должен быть членом базового класса Y (этот поиск применяется, в свою очередь, к Yвключающим классам, начиная с самого внутреннего включающего класса),33 или

  • если X является local class или является вложенным классом локального класса, перед определением класса X в блоке, включающем определение класса X, или

  • if X является членом пространства имен Nили является вложенным классом класса, который является членом N, или является локальным классом или вложенным классом в локальном классе функции, которая является членом N, до использования имени, в пространстве имен N или в одном из Nвключающих пространств имен.

[Example:

class B { };
namespace M {
  namespace N {
    class X : public B {
      void f();
    };
  }
}
void M::N::X::f() {
  i = 16;
}

// The following scopes are searched for a declaration of i:
// 1) outermost block scope of M​::​N​::​X​::​f, before the use of i
// 2) scope of class M​::​N​::​X
// 3) scope of M​::​N​::​X's base class B
// 4) scope of namespace M​::​N
// 5) scope of namespace M
// 6) global scope, before the definition of M​::​N​::​X​::​f

end example] [ Note: [class.mfct] и [class.static] далее опишите ограничения на использование имен в определениях функций-членов. [class.nest] далее описывает ограничения на использование имен в рамках вложенных классов. [class.local] далее описывает ограничения на использование имен в определениях локальных классов. ] end note

Поиск имени для имени, используемого в определении friend function определенного встроенного в классе, предоставляющем дружбу, должен выполняться, как описано для поиска в определениях функций-членов. Если friend функция не определена в классе, предоставляющем дружбу, поиск имени в friend определении функции должен выполняться, как описано для поиска в определениях функций-членов пространства имен.

В friend объявлении, называющем функцию-член, имя, используемое в деклараторе функции, а не часть a template-argument в, declarator-idсначала ищется в области действия class ([class.member.lookup]) функции-члена . Если он не найден или если имя является частью a template-argumentв declarator-id, поиск выполняется так, как описано для неквалифицированных имен в определении класса, предоставляющего дружбу. [Example:

struct A {
  typedef int AT;
  void f1(AT);
  void f2(float);
  template <class T> void f3();
};
struct B {
  typedef char AT;
  typedef float BT;
  friend void A::f1(AT);      // parameter type is A​::​AT
  friend void A::f2(BT);      // parameter type is B​::​BT
  friend void A::f3<AT>();    // template argument is B​::​AT
};

end example]

Во время поиска имени, используемого в качестве default argument в функции parameter-declaration-clauseили используемого в expressionэлементе mem-initializerдля конструктора ([class.base.init]), имена параметров функции видны и скрывают имена сущностей, объявленных в областях блока, класса или пространства имен, содержащих объявление функции. [ Note: [dcl.fct.default] далее описывает ограничения на использование имен в аргументах по умолчанию. [class.base.init] далее описывает ограничения на использование имен в ctor-initializer. ]end note

Во время поиска имени, используемого в constant-expressionобъекте enumerator-definition, ранее объявленные enumerators в перечислении становятся видимыми и скрывают имена сущностей, объявленных в областях блока, класса или пространства имен, содержащих enum-specifier.

Имя, используемое в определении static data member класса X (после qualified-id статического члена) ищется, как если бы имя использовалось в функции-члене класса X. [ Note: [class.static.data] далее описывает ограничения на использование имен в определении элемента static данных. ] end note

Если переменный член пространства имен определен вне области его пространства имен, то любое имя, которое появляется в определении члена (после declarator-id), ищется так, как если бы определение члена произошло в его пространстве имен. [Example:

namespace N {
  int i = 4;
  extern int j;
}

int i = 2;

int N::j = i;       // N​::​j == 4

end example]

Имя, используемое в обработчике для a function-try-block, ищется так, как если бы имя использовалось во внешнем блоке определения функции. В частности, имена параметров функции не должны повторно объявляться exception-declarationни в самом внешнем блоке обработчика, ни в самом внешнем блоке обработчика function-try-block. Имена, объявленные во внешнем блоке определения функции, не обнаруживаются при поиске в области обработчика для function-try-block. [ Note: Но имена параметров функции найдены. ] end note

[ Note: Правила поиска имени в определениях шаблонов описаны в [temp.res]. ] end note

Это относится к неквалифицированным именам, которые встречаются, например, в типе или аргументе по умолчанию в parameter-declaration-clauseили используются в теле функции.

Это относится к неквалифицированным именам, следующим за именем класса; такое имя может использоваться в base-clauseили может использоваться в определении класса.

Этот поиск применяется независимо от того, вложено ли определение X в Yопределение или Xпоявляется ли определение в области пространства имен, включающей Yопределение ([class.nest]).

То есть неквалифицированное имя, которое встречается, например, в типе в parameter-declaration-clauseили в noexcept-specifier.

Этот поиск применяется независимо от того, определена X ли функция-член в определении класса или функция-член определена в области пространства имен, содержащей Xопределение.

6.4.2 Argument-dependent name lookup [basic.lookup.argdep]

Когда postfix-expressionin a function call является an unqualified-id, unqualified lookup могут быть найдены другие пространства имен, не рассматриваемые в обычном режиме , и в этих пространствах имен могут быть найдены дружественные функции области пространства имен или объявления шаблонов функций ([class.friend]), которые иным образом не видны. Эти изменения в поиске зависят от типов аргументов (а для аргументов шаблона шаблона - пространства имен аргумента шаблона). [Example:

namespace N {
  struct S { };
  void f(S);
}

void g() {
  N::S s;
  f(s);     // OK: calls N​::​f
  (f)(s);   // error: N​::​f not considered; parentheses prevent argument-dependent lookup
}

end example]

Для каждого типа аргумента T в вызове функции существует набор из нуля или более associated namespaces и набор из нуля или более, associated classes которые следует учитывать. Наборы пространств имен и классов полностью определяются типами аргументов функции (и пространством имен любого аргумента шаблона шаблона). Имена Typedef, using-declarations используемые для указания типов, не участвуют в этом наборе. Наборы пространств имен и классов определяются следующим образом:

  • Если T это фундаментальный тип, связанные с ним наборы пространств имен и классов пусты.

  • Если T это тип класса (включая объединения), то связанные с ним классы: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Связанные с ним пространства имен - это самые внутренние включающие пространства имен связанных с ним классов. Кроме того, если T это специализация шаблона класса, связанные с ней пространства имен и классы также включают: пространства имен и классы, связанные с типами аргументов шаблона, предоставленными для параметров типа шаблона (исключая параметры шаблона шаблона); пространства имен, членами которых являются любые аргументы шаблона шаблона; и классы, членами которых являются любые шаблоны-элементы, используемые в качестве аргументов шаблона шаблона. [ Note: Аргументы шаблона, не являющиеся типом, не влияют на набор связанных пространств имен. ]end note

  • Если T это тип перечисления, связанное с ним пространство имен является самым внутренним охватывающим пространством имен его объявления. Если это член класса, связанный с ним класс - это класс члена; иначе у него нет ассоциированного класса.

  • Если T это указатель U или массив U, связанные с ним пространства имен и классы - это те, с которыми связаны U.

  • Если T это тип функции, связанные с ним пространства имен и классы - это те, которые связаны с типами параметров функции и те, которые связаны с типом возвращаемого значения.

  • Если T это указатель на функцию-член класса X, связанные с ним пространства имен и классы - это те, которые связаны с типами параметров функции и типом возвращаемого значения, вместе с теми, которые связаны с X.

  • Если T это указатель на член данных класса X, связанные с ним пространства имен и классы - это те, которые связаны с типом члена вместе с теми, которые связаны с X.

Если связанное пространство имен - это inline namespaceпространство имен, в которое оно входит, также включается в набор. Если связанное пространство имен непосредственно содержит встроенные пространства имен, эти встроенные пространства имен также включаются в набор. Кроме того, если аргумент является именем или адресом набора перегруженных функций и / или шаблонов функций, связанные с ним классы и пространства имен представляют собой объединение классов и пространств имен, связанных с каждым из членов набора, т. Е. Связанных классов и пространств имен. с типами параметров и возвращаемым типом. Кроме того, если вышеупомянутый набор перегруженных функций назван с template-id, связанные с ним классы и пространства имен также включают классы и пространства имен этого типа template-arguments и его шаблона template-arguments.

Пусть X будет набором поиска, созданным, unqualified lookup и пусть Y будет набором поиска, созданным поиском, зависимым от аргументов (определенным следующим образом). Если X содержит

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

  • объявление функции блочной области, которое не является using-declaration, или

  • объявление, которое не является ни функцией, ни шаблоном функции

то Y пусто. В противном случае Y набор объявлений находится в пространствах имен, связанных с типами аргументов, как описано ниже. Набор объявлений, найденный при поиске имени, представляет собой объединение X и Y. [ Note: Пространства имен и классы, связанные с типами аргументов, могут включать пространства имен и классы, уже учтенные обычным неквалифицированным поиском. ] [ end noteExample:

namespace NS {
  class T { };
  void f(T);
  void g(T, int);
}
NS::T parm;
void g(NS::T, float);
int main() {
  f(parm);                      // OK: calls NS​::​f
  extern void g(NS::T, float);
  g(parm, 1);                   // OK: calls g(NS​::​T, float)
}

end example]

При рассмотрении связанного пространства имен поиск такой же, как поиск, выполняемый, когда связанное пространство имен используется в качестве квалификатора ([namespace.qual]), за исключением того, что:

  • Любые using-directives элементы в связанном пространстве имен игнорируются.

  • Любые дружественные функции в области видимости пространства имен или шаблоны дружественных функций, объявленные в связанных классах, видны в их соответствующих пространствах имен, даже если они не видны во время обычного поиска ([class.friend]).

  • Все имена, кроме (возможно, перегруженных) функций и шаблонов функций, игнорируются.

6.4.3 Qualified name lookup [basic.lookup.qual]

На имя класса или члена пространства имен или перечислителя можно ссылаться после ​::​ применения оператора разрешения области ([expr.prim]) к объекту, nested-name-specifierкоторый обозначает его класс, пространство имен или перечисление. Если перед ​::​ оператором разрешения области в a nested-name-specifierне стоит decltype-specifier, поиск предшествующего имени ​::​ учитывает только пространства имен, типы и шаблоны, специализацией которых являются типы. Если найденное имя не обозначает пространство имен или класс, перечисление или зависимый тип, программа имеет неправильный формат. [Example:

class A {
public:
  static int n;
};
int main() {
  int A;
  A::n = 42;        // OK
  A b;              // ill-formed: A does not name a type
}

end example]

[ Note: Множественные уточненные имена, например,, N1​::​N2​::​N3​::​nмогут использоваться для обозначения членов вложенных классов ([class.nest]) или членов вложенных пространств имен. ] end note

В объявлении, в котором declarator-idесть a qualified-id, имена, использованные до qualified-id объявления, ищутся в определяющей области пространства имен; имена, следующие за qualified-id, ищутся в области класса или пространства имен члена. [Example:

class X { };
class C {
  class X { };
  static const int number = 50;
  static X arr[number];
};
X C::arr[number];   // ill-formed:
                    // equivalent to ​::​X C​::​arr[C​::​number];
                    // and not to C​::​X C​::​arr[C​::​number];

end example]

Имя с префиксом унарного оператора области видимости ​::​ ([expr.prim]) ищется в глобальной области видимости в той единице перевода, в которой оно используется. Имя должно быть объявлено в области глобального пространства имен или должно быть именем, объявление которого видно в глобальной области видимости из-за using-directive([namespace.qual]). Использование ​::​ позволяет ссылаться на глобальное имя, даже если его идентификатор был hidden.

Имя с префиксом a nested-name-specifier, обозначающее тип перечисления, должно представлять собой элемент enumerator этого перечисления.

Если a pseudo-destructor-name([expr.pseudo]) содержит a nested-name-specifier, type-names ищутся типы в области, обозначенной nested-name-specifier. Аналогично в qualified-idформе:

nested-name-specifieropt class-name :: ~ class-name

второй class-nameищется в той же области, что и первый. [Example:

struct C {
  typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
p->C::I::~I();      // I is looked up in the scope of C
q->I1::~I2();       // I2 is looked up in the scope of the postfix-expression

struct A {
  ~A();
};
typedef A AB;
int main() {
  AB* p;
  p->AB::~AB();     // explicitly calls the destructor for A
}

end example] [ Note: [basic.lookup.classref] Описывает , как имя подстановки после того , как протекает . и -> операторов. ] end note

6.4.3.1 Class members [class.qual]

Если nested-name-specifierиз a qualified-id назначает класс, имя, указанное после, nested-name-specifierищется в области действия class ([class.member.lookup]), за исключением случаев, перечисленных ниже. Имя должно представлять один или несколько членов этого класса или одного из его базовых классов (пункт [class.derived]). [К Note: члену класса можно обратиться с помощью a qualified-idв любой точке его потенциальной области видимости ([basic.scope.class]). ] Исключения из приведенного выше правила поиска имен следующие: end note

В поиске, в котором имена функций не игнорируются,34 а nested-name-specifierкласс назначает C:

вместо этого считается, что имя является именем конструктора класса C. [ Note: Например, конструктор не является приемлемым результатом поиска, elaborated-type-specifierпоэтому конструктор не будет использоваться вместо введенного имени класса. ] Такое имя конструктора должно использоваться только в объявлении, которое называет конструктор, или в . [ end notedeclarator-idusing-declarationExample:

struct A { A(); };
struct B: public A { B(); };

A::A() { }
B::B() { }

B::A ba;            // object of type A
A::A a;             // error, A​::​A is not a type name
struct A::A a2;     // object of type A

end example]

Имя члена класса, скрытое именем во вложенной декларативной области или именем члена производного класса, все еще можно найти, если оно уточняется именем его класса, за которым следует ​::​ оператор.

Поисковые запросы, в которых имена функций игнорируются, включают имена, содержащиеся в a nested-name-specifier, an elaborated-type-specifierили a base-specifier.

6.4.3.2 Namespace members [namespace.qual]

Если nested-name-specifiera qualified-id назначает пространство имен (включая случай, когда nested-name-specifieris ​::​, т. Е. Назначает глобальное пространство имен), имя, указанное после a, ищется nested-name-specifierв области пространства имен. Имена в a template-argumentили a template-idищутся в контексте, в котором postfix-expressionвстречается целое .

Для пространства имен X и имени mнабор поиска с указанием пространства имен S(X,m) определяется следующим образом: Пусть S(X,m) будет набором всех объявлений m in X и inline namespace set of X. Если S(X,m) не пусто, то S(X,m) есть S(X,m); в противном случае S(X,m) - это объединение S(Ni,m) всех пространств имен, Ni назначенных using-directives in, X и его встроенного набора пространств имен.

Задано X​::​m (где X - объявленное пользователем пространство имен) или задано ​::​m (где X - глобальное пространство имен), если S(X,m) это пустой набор, программа плохо сформирована. В противном случае, если S(X,m) имеет ровно один член или если контекст ссылки является a using-declaration, S(X,m) является требуемым набором объявлений m. В противном случае, если использование m не позволяет выбрать уникальное объявление S(X,m), программа имеет неправильный формат. [Example:

int x;
namespace Y {
  void f(float);
  void h(int);
}

namespace Z {
  void h(double);
}

namespace A {
  using namespace Y;
  void f(int);
  void g(int);
  int i;
}

namespace B {
  using namespace Z;
  void f(char);
  int i;
}

namespace AB {
  using namespace A;
  using namespace B;
  void g();
}

void h()
{
  AB::g();      // g is declared directly in AB, therefore S is { AB​::​g() } and AB​::​g() is chosen

  AB::f(1);     // f is not declared directly in AB so the rules are applied recursively to A and B;
                // namespace Y is not searched and Y​::​f(float) is not considered;
                // S is {A::f(int),B::f(char)} and overload resolution chooses A​::​f(int)

  AB::f('c');   // as above but resolution chooses B​::​f(char)

  AB::x++;      // x is not declared directly in AB, and is not declared in A or B, so the rules
                // are applied recursively to Y and Z, S is { } so the program is ill-formed

  AB::i++;      // i is not declared directly in AB so the rules are applied recursively to A and B,
                // S is {A::i,B::i} so the use is ambiguous and the program is ill-formed

  AB::h(16.8);  // h is not declared directly in AB and not declared directly in A or B so the rules
                // are applied recursively to Y and Z, S is {Y::h(int),Z::h(double)} and
                // overload resolution chooses Z​::​h(double)
}

end example]

[ Note: Одно и то же объявление, найденное более одного раза, не является двусмысленностью (потому что это все еще уникальное объявление). [Example:

namespace A {
  int a;
}

namespace B {
  using namespace A;
}

namespace C {
  using namespace A;
}

namespace BC {
  using namespace B;
  using namespace C;
}

void f()
{
  BC::a++;          // OK: S is {A::a,A::a}
}

namespace D {
  using A::a;
}

namespace BD {
  using namespace B;
  using namespace D;
}

void g()
{
  BD::a++;          // OK: S is {A::a,A::a}
}

end example] ]end note

[ Example: Поскольку каждое указанное пространство имен просматривается не более одного раза, следующее четко определено:

namespace B {
  int b;
}

namespace A {
  using namespace B;
  int a;
}

namespace B {
  using namespace A;
}

void f()
{
  A::a++;           // OK: a declared directly in A, S is { A​::​a }
  B::a++;           // OK: both A and B searched (once), S is { A​::​a }
  A::b++;           // OK: both A and B searched (once), S is { B​::​b }
  B::b++;           // OK: b declared directly in B, S is { B​::​b }
}

end example]

Во время поиска полного имени члена пространства имен, если поиск находит более одного объявления члена, и если одно объявление вводит имя класса или имя перечисления, а другие объявления либо вводят ту же переменную, тот же перечислитель или набор функции, имя, не являющееся типом, скрывает имя класса или перечисления тогда и только тогда, когда объявления взяты из одного и того же пространства имен; в противном случае (объявления взяты из разных пространств имен) программа имеет неправильный формат. [Example:

namespace A {
  struct x { };
  int x;
  int y;
}

namespace B {
  struct y { };
}

namespace C {
  using namespace A;
  using namespace B;
  int i = C::x;     // OK, A​::​x (of type int)
  int j = C::y;     // ambiguous, A​::​y or B​::​y
}

end example]

В объявлении для члена пространства имен, в котором declarator-idесть qualified-id, при условии, что qualified-idдля члена пространства имен имеет форму

nested-name-specifier unqualified-id

unqualified-idбудет называть элемент пространства имен обозначены той nested-name-specifier или элемент из inline namespace set этого пространства имен. [Example:

namespace A {
  namespace B {
    void f1(int);
  }
  using namespace B;
}
void A::f1(int){ }  // ill-formed, f1 is not a member of A

end example] Однако в таких объявлениях членов пространства имен объект nested-name-specifierможет using-directives неявно предоставить начальную часть nested-name-specifier. [Example:

namespace A {
  namespace B {
    void f1(int);
  }
}

namespace C {
  namespace D {
    void f1(int);
  }
}

using namespace A;
using namespace C::D;
void B::f1(int){ }  // OK, defines A​::​B​::​f1(int)

end example]

6.4.4 Elaborated type specifiers [basic.lookup.elab]

elaborated-type-specifierМожет быть использовано для обозначения предварительно объявленных class-nameили enum-nameхотя имя было hidden в заявлении не-типа.

Если elaborated-type-specifierнет nested-name-specifier, и если он не elaborated-type-specifierуказан в декларации следующей формы:

class-key attribute-specifier-seqopt identifier ;

identifierищется в соответствии [basic.lookup.unqual] но игнорируя любые имена не типа , которые были объявлены. Если elaborated-type-specifierвводится enum ключевым словом, и этот поиск не находит ранее объявленного type-name, то elaborated-type-specifier это неправильно сформировано. Если elaborated-type-specifierвводится с помощью, class-keyи этот поиск не находит ранее объявленного type-name, или если elaborated-type-specifierпоявляется в объявлении с формой:

class-key attribute-specifier-seqopt identifier ;

elaborated-type-specifierявляется свидетельством того, что вводит , class-nameкак описано в [basic.scope.pdecl].

Если elaborated-type-specifierесть nested-name-specifier, выполняется поиск квалифицированного имени, как описано в разделе [basic.lookup.qual], но игнорируя любые объявленные не-типовые имена. Если поиск по имени не находит ранее объявленного type-name, elaborated-type-specifier значит, неправильно сформирован. [Example:

struct Node {
  struct Node* Next;            // OK: Refers to Node at global scope
  struct Data* Data;            // OK: Declares type Data
                                // at global scope and member Data
};

struct Data {
  struct Node* Node;            // OK: Refers to Node at global scope
  friend struct ::Glob;         // error: Glob is not declared, cannot introduce a qualified type ([dcl.type.elab])
  friend struct Glob;           // OK: Refers to (as yet) undeclared Glob at global scope.
  /* ... */
};

struct Base {
  struct Data;                  // OK: Declares nested Data
  struct ::Data*     thatData;  // OK: Refers to ​::​Data
  struct Base::Data* thisData;  // OK: Refers to nested Data
  friend class ::Data;          // OK: global Data is a friend
  friend class Data;            // OK: nested Data is a friend
  struct Data { /* ... */ };    // Defines nested Data
};

struct Data;                    // OK: Redeclares Data at global scope
struct ::Data;                  // error: cannot introduce a qualified type ([dcl.type.elab])
struct Base::Data;              // error: cannot introduce a qualified type ([dcl.type.elab])
struct Base::Datum;             // error: Datum undefined
struct Base::Data* pBase;       // OK: refers to nested Data

end example]

6.4.5 Class member access [basic.lookup.classref]

В class member access выражении, если за токеном . или -> сразу следует символ, за которым identifier следует символ <, необходимо найти идентификатор, чтобы определить, < является ли это началом списка аргументов шаблона ([temp.names]) или оператором «меньше». Идентификатор сначала ищется в классе выражения объекта. Если идентификатор не найден, он затем ищется в контексте всего postfix-expressionи должен дать имя шаблону класса.

Если id-expressionin a class member access - это unqualified-id, а тип выражения объекта относится к типу класса C, unqualified-idищется в области действия класса C. Для pseudo-destructor call, то unqualified-idищется в контексте полной postfix-expression.

Если unqualified-idесть , то ищется в контексте всего . Если тип объектного выражения относится к типу класса , он также ищется в области действия класса . По крайней мере, один из поисков должен найти имя, которое относится к . [ ~type-nametype-namepostfix-expression T Ctype-name C cv TExample:

struct A { };

struct B {
  struct A { };
  void f(::A* a);
};

void B::f(::A* a) {
  a->~A();                      // OK: lookup in *a finds the injected-class-name
}

end example]

Если id-expressionдоступ для члена класса имеет qualified-idформу

class-name-or-namespace-name::...

class-name-or-namespace-name следуя . или -> оператор сначала ищется в классе выражения объекта и имя, если он найден, используется. В противном случае он ищется в контексте всего postfix-expression. [ Note: См. [basic.lookup.qual], Где описан поиск имени ранее ​::​, при котором будет найден только тип или имя пространства имен. ] end note

Если qualified-idимеет вид

::class-name-or-namespace-name::...

class-name-or-namespace-name ищется в глобальном масштабе в виде class-nameили namespace-name.

Если nested-name-specifierсодержит a simple-template-id, имена в нем template-arguments ищутся в контексте, в котором postfix-expressionвстречается все.

Если id-expressionэто a conversion-function-id, его conversion-type-id сначала ищут в классе выражения объекта, и имя, если оно найдено, используется. В противном случае он ищется в контексте всего postfix-expression. В каждом из этих поисков учитываются только имена, обозначающие типы или шаблоны, специализацией которых являются типы. [Example:

struct A { };
namespace N {
  struct A {
    void g() { }
    template <class T> operator T();
  };
}

int main() {
  N::A a;
  a.operator A();               // calls N​::​A​::​operator N​::​A
}

end example]

6.4.6 Using-directives and namespace aliases [basic.lookup.udir]

В using-directiveor namespace-alias-definition, во время поиска namespace-nameили для имени в nested-name-specifier пространстве имен учитываются только имена.

6.6 Start and termination [basic.start]

6.6.1 main function [basic.start.main]

Программа должна содержать глобальную функцию с именем main. Выполнение программы запускает основной поток выполнения ([intro.multithread], [thread.threads]), в котором main вызывается функция и в котором переменные статической продолжительности хранения могут быть инициализированы ([basic.start.static]) и уничтожены ([basic.start.term]). Это определяется реализацией, требуется ли программа в автономной среде для определения main функции. [ Note: В автономной среде запуск и завершение определяются реализацией; start-up содержит выполнение конструкторов для объектов области пространства имен со статической продолжительностью хранения; завершение содержит выполнение деструкторов для объектов со статической продолжительностью хранения. ] end note

Реализация не должна предопределять main функцию. Эта функция не должна быть перегружена. Его тип должен иметь связь с языком C ++, и он должен иметь объявленный тип возвращаемого значения int, но в остальном его тип определяется реализацией. Реализация должна позволять как

  • функция () возврата int и

  • функция (int, указатель на указатель на char) возврат int

как тип main ([dcl.fct]). В последней форме, с целью демонстрации, вызывается первый параметр функции и вызывается второй параметр функции , где должно быть количество аргументов, переданных программе из среды, в которой программа выполняется. Если не равно нулю эти аргументы должны быть поставлены в счете в качестве указателей на начальные символы с завершающим нулевыми мультибайтными строками ( ntmbs с) ( ) и должны быть указателем на начальный характер ntmbs , который представляет собой имя , используемое для запуска программы или . Значение должно быть неотрицательным. Значение должно быть 0. [ Рекомендуется добавлять любые дополнительные (необязательные) параметры после . ] argc argv argc argc argv[0] argv[argc-1] [multibyte.strings] argv[0] "" argc argv[argc] Note: argv end note

Функцию main нельзя использовать в программе. Из вне реализации. Программа , которая определяет , как удаленные или что декларирует быть , или плохо сформированным. Функция не должна быть объявлена с . Программа, которая объявляет переменную в глобальной области видимости или объявляет имя с привязкой к языку C (в любом пространстве имен), имеет неправильный формат. Имя не зарезервировано. [ Функции-члены, классы и перечисления могут быть вызваны , как и сущности в других пространствах имен. ] linkage main main main inline static constexpr main linkage-specification main main main Example: main end example

Завершение программы без выхода из текущего блока (например, путем вызова функции std​::​exit(int)) не уничтожает никаких объектов с автоматической продолжительностью хранения ([class.dtor]). Если std​::​exit вызывается для завершения программы во время уничтожения объекта со статической продолжительностью хранения или продолжительностью хранения потока, программа имеет неопределенное поведение.

Оператор return in main имеет эффект выхода из основной функции (уничтожение любых объектов с автоматической продолжительностью хранения) и вызова std​::​exit с возвращаемым значением в качестве аргумента. Если управление потоками от конца compound-statementиз main, эффект эквивалентен return с операндом 0 (также см [except.handle]).

6.6.2 Static initialization [basic.start.static]

Переменные со статической продолжительностью хранения инициализируются как следствие запуска программы. Переменные с продолжительностью хранения потока инициализируются как следствие выполнения потока. В рамках каждой из этих фаз инициализации инициализация происходит следующим образом.

A constant initializer для переменной или временного объекта o - это инициализатор, полное выражение которого является константным выражением, за исключением того, что если o это объект, такой инициализатор может также вызывать конструкторы constexpr для o и его подобъектов, даже если эти объекты относятся к нелитеральным типам классов. [ Note: Такой класс может иметь нетривиальный деструктор. ] выполняется, если переменный или временный объект со статической продолжительностью хранения или хранением потока инициализируется константным инициализатором для сущности. Если постоянная инициализация не выполняется, переменная с или есть . Вместе вызываются инициализация нулем и инициализация константой ; все остальные инициализации есть . Всякая статическая инициализация строго происходит до ( ) любой динамической инициализации. [ Динамическая инициализация нелокальных переменных описана в ; локальные статические переменные описаны в . ] end noteConstant initialization static storage duration thread storage duration zero-initializedstatic initialization dynamic initialization[intro.races]Note: [basic.start.dynamic] [stmt.dcl] end note

Реализации разрешается выполнять инициализацию переменной со статической продолжительностью хранения или хранения потока в качестве статической инициализации, даже если такая инициализация не требуется статически, при условии, что

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

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

[ Note: Как следствие, если инициализация объекта obj1 относится к объекту obj2 области пространства имен, потенциально требующему динамической инициализации и определенному позже в той же единице преобразования, не указано, будет ли значение obj2 used значением полностью инициализированного obj2 (поскольку obj2 было статически инициализирован) или будет obj2 просто нулевым значением. Например,

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1;     // unspecified:
                    // may be statically initialized to 0.0 or
                    // dynamically initialized to 0.0 if d1 is
                    // dynamically initialized, or 1.0 otherwise
double d1 = fd();   // may be initialized statically or dynamically to 1.0

end note]

6.6.3 Dynamic initialization of non-local variables [basic.start.dynamic]

Динамическая инициализация нелокальной переменной со статической продолжительностью хранения неупорядочена, если переменная является неявно или явно созданной специализацией, частично упорядочена, если переменная является встроенной переменной, которая не является неявно или явно конкретизированной специализацией, и в противном случае упорядочена . [ Note: Явно специализированный не встроенный статический член данных или специализация шаблона переменной упорядочили инициализацию. ]end note

Динамическая инициализация нелокальных переменных V и W со статической продолжительностью хранения упорядочены следующим образом :

  • Если V и W имеют упорядоченную инициализацию и V определены ранее W в одной единице трансляции, инициализация V выполняется в последовательности перед инициализацией W.

  • Если V имеет частично упорядоченную инициализацию, W не имеет неупорядоченной инициализации и V определен ранее W в каждой единице перевода, в которой W определена, то

    • если программа запускает поток ([intro.multithread]), отличный от основного потока ([basic.start.main]), инициализация V строго выполняется до инициализации W;

    • в противном случае инициализация V выполняется до инициализации W.

  • В противном случае, если программа запускает поток, отличный от основного, до инициализации V или W , то не указано, в каких потоках происходит инициализация V и W происходит; инициализации не имеют последовательности, если они происходят в одном потоке.

  • В противном случае инициализации V и W имеют неопределенную последовательность.

[ Note: Это определение позволяет инициализировать последовательность упорядоченных переменных одновременно с другой последовательностью. ]end note

A non-initialization odr-use - это odr-use ([basic.def.odr]), не вызванная прямо или косвенно инициализацией нелокальной статической переменной или переменной продолжительности хранения потока.

Это определяется реализацией, будет ли динамическая инициализация нелокальной внелинейной переменной со статической продолжительностью хранения последовательно перед первым оператором main или откладывается. Если это отложено, это обязательно произойдет до любого неинициализационного odr-использования любой не встроенной функции или не встроенной переменной, определенной в той же единице перевода, что и инициализируемая переменная.36 Это определяется реализацией, в каких потоках и в каких точках программы происходит такая отложенная динамическая инициализация. [ Note: Такие точки следует выбирать таким образом, чтобы позволить программисту избежать взаимоблокировок. ] [end noteExample:

// - File 1 -
#include "a.h"
#include "b.h"
B b;
A::A(){
  b.Use();
}

// - File 2 -
#include "a.h"
A a;

// - File 3 -
#include "a.h"
#include "b.h"
extern A a;
extern B b;

int main() {
  a.Use();
  b.Use();
}

Это определяется реализацией ли либо a или b инициализируется до main ввода , или задерживаются на инициализацию до тех пор , a пока первый ODR используемый в main. В частности, если a инициализируется до main ввода, не гарантируется, что b будет инициализирован до того, как он будет использован odr при инициализации a, то есть перед вызовом A​::​A . Если, однако, a инициализируется в какой-то момент после первого оператора main, b будет инициализирован до его использования в A​::​A. ] end example

Это определяется реализацией, будет ли динамическая инициализация нелокальной встроенной переменной со статической продолжительностью хранения упорядочена до первого оператора main или откладывается. Если это отложено, это обязательно произойдет до любого использования этой переменной без инициализации. Это определяется реализацией, в каких потоках и в каких точках программы происходит такая отложенная динамическая инициализация.

Это определяется реализацией, будет ли динамическая инициализация нелокальной не встроенной переменной с продолжительностью хранения потока последовательной перед первым оператором начальной функции потока или откладывается. Если это отложено, инициализация, связанная с объектом для потока t , упорядочивается перед первым использованием неинициализации odr t любой не встроенной переменной с продолжительностью хранения потока, определенной в той же единице перевода, что и инициализируемая переменная. Это определяется реализацией, в каких потоках и в каких точках программы происходит такая отложенная динамическая инициализация.

Если инициализация нелокальной переменной со статической продолжительностью хранения или продолжительностью хранения потока завершается через исключение, std​::​terminate вызывается.

В этом случае инициализируется нелокальная переменная со статической продолжительностью хранения, имеющая инициализацию с побочными эффектами, даже если она сама не используется odr ([basic.def.odr], [basic.stc.static]).

6.6.4 Termination [basic.start.term]

Destructors для инициализированных объектов (то есть объектов, которые lifetime были начаты) со статической продолжительностью хранения, и функции, зарегистрированные с помощью std​::​atexit, вызываются как часть вызова . Вызов выполняется до вызова деструкторов и зарегистрированных функций. [ Возврат из invokes ( ). ]std​::​exit std​::​exit Note: main std​::​exit [basic.start.main]end note

Деструкторы для инициализированных объектов с продолжительностью хранения потока в данном потоке вызываются в результате возврата из начальной функции этого потока и в результате вызова этого потока std​::​exit. Завершение деструкторов для всех инициализированных объектов с длительностью хранения потока в этом потоке строго происходит до инициирования деструкторов любого объекта со статической продолжительностью хранения.

Если завершение конструктора или динамическая инициализация объекта со статической продолжительностью хранения сильно происходит раньше, чем у другого, завершение деструктора второго упорядочивается до инициирования деструктора первого. Если завершение конструктора или динамическая инициализация объекта с продолжительностью хранения потока происходит раньше, чем у другого, завершение деструктора второго выполняется до запуска деструктора первого. Если объект инициализируется статически, он уничтожается в том же порядке, как если бы объект был инициализирован динамически. Для объекта типа массива или класса все подобъекты этого объекта уничтожаются до того, как будет уничтожен любой объект блочной области со статической продолжительностью хранения, инициализированный во время построения подобъектов. Если уничтожение объекта со статической продолжительностью хранения или продолжительностью хранения потока завершается через исключение, std​::​terminate вызывается.

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

Если завершение инициализации объекта со статической продолжительностью хранения происходит строго перед вызовом std​::​atexit (см. <cstdlib>, [support.start.term]), Вызов переданной функции std​::​atexit упорядочивается до вызова деструктора для объекта. Если вызов std​::​atexit строго происходит до завершения инициализации объекта со статической продолжительностью хранения, вызов деструктора для объекта упорядочивается до вызова переданной функции std​::​atexit. Если вызов std​::​atexit строго происходит перед другим вызовом std​::​atexit, вызов функции, переданной второму std​::​atexit вызову, упорядочивается до вызова функции, переданной первому std​::​atexit вызову.

Если в обработчиках сигналов ([support.runtime]) не разрешено использование объекта стандартной библиотеки или функции , которая не happen before завершает уничтожение объектов со статической продолжительностью хранения и выполнение std​::​atexit зарегистрированных функций ([support.start.term]), программа имеет неопределенное поведение. [ Note: Если есть использование объекта со статической продолжительностью хранения, которое не происходит до разрушения объекта, программа имеет неопределенное поведение. Завершение каждого потока перед вызовом std​::​exit или выходом из main него достаточно, но не обязательно, чтобы удовлетворить этим требованиям. Эти требования разрешают диспетчеры потоков как объекты статической продолжительности хранения. ] end note

Вызов функции, std​::​abort() объявленной в, <cstdlib> завершает программу без выполнения каких-либо деструкторов и без вызова функций, переданных в std​::​atexit() или std​::​at_­quick_­exit().

6.7 Storage duration [basic.stc]

Это storage duration свойство объекта, которое определяет минимальное потенциальное время жизни хранилища, содержащего объект. Продолжительность хранения определяется конструкцией, используемой для создания объекта, и является одной из следующих:

  • статическая продолжительность хранения

  • продолжительность хранения потока

  • автоматическая продолжительность хранения

  • продолжительность динамического хранения

Длительности статического, потокового и автоматического хранения связаны с объектами, введенными с помощью объявлений ([basic.def]) и implicitly created by the implementation. Продолжительность динамического хранения связана с объектами, созданными файлом new-expression.

Категории продолжительности хранения применимы и к ссылкам.

Когда достигается конец продолжительности области хранения, значения всех указателей, представляющих адрес любой части этой области хранения, становятся invalid pointer values. Косвенное обращение через недопустимое значение указателя и передача недопустимого значения указателя в функцию освобождения имеют неопределенное поведение. Любое другое использование недопустимого значения указателя имеет поведение, определяемое реализацией.37

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

6.7.1 Static storage duration [basic.stc.static]

Все переменные, которые не имеют динамической продолжительности хранения, не имеют продолжительности хранения потоков и не являются локальными, имеют static storage duration. Хранение этих объектов должно длиться на время программы ([basic.start.static], [basic.start.term]).

Если переменная со статической продолжительностью хранения имеет инициализацию или деструктор с побочными эффектами, она не должна удаляться, даже если кажется, что она не используется, за исключением того, что объект класса или его копия / перемещение могут быть удалены, как указано в [class.copy].

Ключевое слово static можно использовать для объявления локальной переменной со статической продолжительностью хранения. [ Note: [stmt.dcl] описывает инициализацию локальных static переменных; [basic.start.term] описывает уничтожение локальных static переменных. ] end note

Ключевое слово, static примененное к члену данных класса в определении класса, дает статическую продолжительность хранения члена данных.

6.7.2 Thread storage duration [basic.stc.thread]

Все переменные, объявленные с thread_­local ключевым словом, имеют thread storage duration. Хранение этих объектов должно длиться в течение всего потока, в котором они созданы. Для каждого потока существует отдельный объект или ссылка, и использование объявленного имени относится к сущности, связанной с текущим потоком.

Переменная с продолжительностью хранения потока должна быть инициализирована до ее первой odr-use и, если она создана, должна быть уничтожена при выходе из потока.

6.7.3 Automatic storage duration [basic.stc.auto]

Блок-область видимости переменных явно не объявлены static, thread_­localили extern есть automatic storage duration. Хранилище для этих сущностей длится до тех пор, пока блок, в котором они созданы, не завершится.

[ Note: Эти переменные инициализируются и уничтожаются, как описано в [stmt.dcl]. ]end note

Если переменная с автоматической продолжительностью хранения имеет инициализацию или деструктор с побочными эффектами, реализация не должна уничтожать ее до конца своего блока или исключать ее как оптимизацию, даже если она кажется неиспользуемой, за исключением того, что объект класса или его копирование / перемещение может быть удалено, как указано в [class.copy].

6.7.4 Dynamic storage duration [basic.stc.dynamic]

Объекты можно создавать динамически во время program executionиспользования и уничтожать с помощью . Реализация C ++ обеспечивает доступ и управление динамической памятью через глобальное и глобальное и . [ Формы без выделения памяти, описанные в , не выполняют выделение или освобождение. ]new-expressionsdelete-expressionsallocation functions operator new operator new[] deallocation functions operator delete operator delete[]Note: [new.delete.placement]end note

Библиотека предоставляет определения по умолчанию для функций глобального распределения и освобождения. Некоторые функции глобального распределения и освобождения можно заменить ([new.delete]). Программа на C ++ должна предоставлять не более одного определения заменяемой функции выделения или освобождения. Любое такое определение функции заменяет версию по умолчанию, предоставленную в library ([replacement.functions]). Следующие функции выделения и освобождения ([support.dynamic]) неявно объявляются в глобальной области видимости в каждой единице трансляции программы.

void* operator new(std::size_t);
void* operator new(std::size_t, std::align_val_t);

void operator delete(void*) noexcept;
void operator delete(void*, std::size_t) noexcept;
void operator delete(void*, std::align_val_t) noexcept;
void operator delete(void*, std::size_t, std::align_val_t) noexcept;

void* operator new[](std::size_t);
void* operator new[](std::size_t, std::align_val_t);

void operator delete[](void*) noexcept;
void operator delete[](void*, std::size_t) noexcept;
void operator delete[](void*, std::align_val_t) noexcept;
void operator delete[](void*, std::size_t, std::align_val_t) noexcept;

Эти неявные декларации ввести только имена функций operator new, operator new[], operator deleteи operator delete[]. [ Note: Неявные декларации не вводить имена std, std​::​size_­t, std​::​align_­val_­tили любые другие имена , которые библиотека использует , чтобы объявить эти имена. Таким образом, new-expression, delete-expressionили вызов функции , которая относится к одной из этих функций , не включая заголовок <new> правильно сформирован. Однако ссылка на std or std​::​size_­t или std​::​align_­val_­t неправильно сформирована, если имя не было объявлено путем включения соответствующего заголовка. ] Функции распределения и / или освобождения также могут быть объявлены и определены для любого класса ( ). end note[class.free]

Любые функции распределения и / или освобождения, определенные в программе C ++, включая версии по умолчанию в библиотеке, должны соответствовать семантике, указанной в [basic.stc.dynamic.allocation] и [basic.stc.dynamic.deallocation].

6.7.4.1 Allocation functions [basic.stc.dynamic.allocation]

Функция распределения должна быть функцией-членом класса или глобальной функцией; программа неправильно сформирована, если функция распределения объявлена ​​в области пространства имен, отличной от глобальной, или объявлена ​​статической в ​​глобальной области. Тип возврата должен быть void*. Первый параметр должен иметь тип std​::​size_­t ([support.types]). Первый параметр не должен иметь ассоциированного default argument. Значение первого параметра должно интерпретироваться как запрошенный размер выделения. Функция распределения может быть шаблоном функции. Такой шаблон должен объявлять свой возвращаемый тип и первый параметр, как указано выше (то есть типы параметров шаблона не должны использоваться в типе возвращаемого значения и первом типе параметра). Функции распределения шаблонов должны иметь два или более параметра.

Функция распределения пытается выделить запрошенный объем памяти. В случае успеха он должен вернуть адрес начала блока памяти, длина которого в байтах должна быть не меньше запрошенного размера. Нет ограничений на содержимое выделенной памяти при возврате из функции распределения. Порядок, непрерывность и начальное значение памяти, выделенной последовательными вызовами функции распределения, не определены. Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель на любой подходящий полный тип объекта ([new.delete.single]), а затем использовать для доступа к объекту или массиву в выделенной памяти (до тех пор, пока память не будет явно освобождена вызовом соответствующего функция освобождения). Даже если размер запрошенного пространства равен нулю, запрос может завершиться ошибкой. Если запрос завершается успешно, возвращаемое значение должно быть ненулевым значением указателя ([conv.ptr]), p0 отличным от любого ранее возвращенного значения p1, если это значение не p1 было впоследствии передано в operator delete. Кроме того, для функций распределения библиотек в [new.delete.single] и [new.delete.array], p0 должен представлять адрес блока памяти, отличного от хранилища для любого другого объекта, доступного для вызывающей стороны. Эффект косвенного обращения через указатель, возвращаемый как запрос нулевого размера, не определен.38

Функция распределения, которой не удается выделить хранилище, может вызвать установленную в данный момент функцию нового обработчика ([new.handler]), если таковая имеется. [ Поставляемая программой функция распределения может получить адрес установленного в данный момент с помощью функции ( ). ] Если функция распределения, имеющая не-бросание, не может выделить память, она должна вернуть нулевой указатель. Любая другая функция распределения, которой не удается выделить память, должна указывать на сбой только по типу, который соответствует типу of .Note: new_­handler std​::​get_­new_­handler [set.new.handler] end noteexception specification throwing an exception handler std​::​bad_­alloc

Функция глобального распределения вызывается только в результате a new expression, или function call вызывается напрямую с использованием синтаксиса, или вызывается косвенно через вызовы функций в стандартной библиотеке C ++. [ Note: В частности, функция глобального распределения не вызывается для выделения памяти для объектов с static storage duration, для объектов или ссылок с thread storage duration, для объектов типа std​::​type_­infoили для exception object. ]end note

Намерение состоит в том, чтобы operator new() реализовать его с помощью вызова std​::​malloc() или std​::​calloc(), поэтому правила в основном те же. C ++ отличается от C тем, что требует нулевого запроса для возврата ненулевого указателя.

6.7.4.2 Deallocation functions [basic.stc.dynamic.deallocation]

Функции отмены распределения должны быть функциями-членами класса или глобальными функциями; программа плохо сформирована, если функции освобождения объявлены в области имен, отличной от глобальной, или объявлены статическими в глобальной области.

Каждая функция освобождения должна возвращать, void и ее первый параметр должен быть void*. Функция освобождения может иметь более одного параметра. A usual deallocation function - это функция освобождения, которая имеет:

  • ровно один параметр; или

  • ровно два параметра, тип второго - либо std​::​align_­val_­t или std​::​size_­t39; или

  • ровно три параметра: тип второго существа std​::​size_­t и тип третьего существа std​::​align_­val_­t.

Функция освобождения может быть экземпляром шаблона функции. Ни первый параметр, ни тип возвращаемого значения не должны зависеть от параметра шаблона. [ Note: То есть шаблон функции освобождения должен иметь первый параметр типа void* и тип возвращаемого значения void (как указано выше). ] Шаблон функции освобождения должен иметь два или более функциональных параметра. Экземпляр шаблона никогда не является обычной функцией освобождения, независимо от его сигнатуры. end note

Если функция освобождения завершается выдачей исключения, поведение не определено. Значение первого аргумента, предоставленного функции освобождения, может быть значением нулевого указателя; если это так, и если функция освобождения предоставлена ​​в стандартной библиотеке, вызов не имеет никакого эффекта.

Если аргумент, переданный функции освобождения в стандартной библиотеке, является указателем, который не является указателем, null pointer valueфункция освобождения должна освободить память, на которую ссылается указатель, завершив продолжительность области хранения.

Глобальное значение operator delete(void*, std​::​size_­t) исключает использование функции распределения void operator new(std​::​size_­t, std​::​size_­t) в качестве функции распределения размещения ([diff.cpp11.basic]).

6.7.4.3 Safely-derived pointers [basic.stc.dynamic.safety]

А traceable pointer object это

  • объект object pointer type, или

  • объект интегрального типа размером не менее std​::​intptr_­t, или

  • последовательность элементов в массиве narrow character type, где размер и выравнивание последовательности соответствуют таковым у некоторого типа указателя на объект.

Значение указателя является safely-derived pointer динамическим объектом, только если он имеет тип указателя на объект и является одним из следующих:

  • значение, возвращаемое вызовом реализации стандартной библиотеки C ++ ​::​operator new(std​::​​size_­t) или ​::​operator new(std​::​size_­t, std​::​align_­val_­t);40

  • результат взятия адреса объекта (или одного из его подобъектов), обозначенного lvalue в результате косвенного обращения через безопасно полученное значение указателя;

  • результат четко определенной арифметики указателя ([expr.add]) с использованием безопасно полученного значения указателя;

  • результат четко определенного преобразования указателя ([conv.ptr], [expr.cast]) безопасного значения указателя;

  • результат reinterpret_­cast безопасного получения значения указателя;

  • результат reinterpret_­cast целочисленного представления безопасного значения указателя;

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

Целочисленное значение является значением integer representation of a safely-derived pointer только в том случае, если его тип не меньше размера, std​::​intptr_­t и это одно из следующих значений:

  • результат reinterpret_­cast безопасного получения значения указателя;

  • результат действительного преобразования целочисленного представления безопасного значения указателя;

  • значение объекта, значение которого было скопировано из отслеживаемого объекта-указателя, где во время копирования исходный объект содержал целочисленное представление безопасно полученного значения указателя;

  • результат аддитивной или побитовой операции, один из операндов которой является целочисленным представлением безопасно полученного значения указателя P, если этот результат, преобразованный с помощью, reinterpret_­cast<void*> будет сравниваться с безопасным полученным указателем, вычисляемым из reinterpret_­cast<void*>(P).

Реализация может иметь relaxed pointer safety, и в этом случае действительность значения указателя не зависит от того, является ли это безопасным значением указателя. В качестве альтернативы, реализация может иметь strict pointer safety, и в этом случае значение указателя, относящееся к объекту с длительностью динамического хранения, которое не является безопасным значением указателя, является недопустимым значением указателя, если указанный полный объект не был ранее объявлен достижимым ([util.dynamic.safety]). [ Note: Эффект от использования недопустимого значения указателя (включая передачу его в функцию освобождения) не определен, см [basic.stc.dynamic.deallocation]. Это верно, даже если значение указателя, полученное небезопасно, может сравниться с некоторым значением указателя, полученным безопасным способом. ] Это определяется реализацией, имеет ли реализация ослабленную или строгую безопасность указателя. end note

Этот раздел не налагает ограничений на косвенное обращение через указатели к памяти, не выделенной ​::​operator new. Это поддерживает способность многих реализаций C ++ использовать двоичные библиотеки и компоненты, написанные на других языках. В частности, это относится к двоичным файлам C, потому что косвенное обращение через указатели к памяти, выделенной им std​::​malloc , не ограничено.

6.7.5 Duration of subobjects [basic.stc.inherit]

Продолжительность хранения подобъектов и ссылочных элементов - это время хранения их полного объекта ([intro.object]).

6.8 Object lifetime [basic.life]

lifetime Объект или ссылок является средой свойства объекта или ссылки. Считается, что объект имеет non-vacuous initialization тип класса или агрегата и он или один из его подобъектов инициализируется конструктором, отличным от тривиального конструктора по умолчанию. [ Note: Инициализация с помощью тривиального конструктора копирования / перемещения не является пустой инициализацией. ] Время жизни объекта типа начинается, когда: end note T

  • T получается хранилище с надлежащим выравниванием и размером для типа , и

  • если объект имеет непустую инициализацию, его инициализация завершена,

за исключением того, что если объект является членом объединения или его подобъектом, его время жизни начинается только в том случае, если этот член объединения является инициализированным членом в объединении ([dcl.init.aggr], [class.base.init]) или как описано в [class.union]. Время жизни объекта o типа T заканчивается, когда:

  • если T это тип класса с нетривиальным деструктором ([class.dtor]), запускается вызов деструктора, или

  • память, которую занимает объект, освобождается или повторно используется объектом, который не вложен в o ([intro.object]).

Время жизни ссылки начинается после завершения ее инициализации. Время жизни ссылки заканчивается, как если бы она была скалярным объектом.

[ Note: [class.base.init] описывает время жизни базовых и членских подобъектов. ] end note

Свойства, приписываемые объектам и ссылкам в настоящем международном стандарте, применяются к данному объекту или ссылке только в течение его срока службы. [ Note: В частности, до начала жизненного цикла объекта и после его окончания существуют значительные ограничения на использование объекта, как описано ниже, в [class.base.init] и в [class.cdtor]. Кроме того, поведение строящегося и разрушаемого объекта может отличаться от поведения объекта, время существования которого началось и не закончилось. [class.base.init] и [class.cdtor] описывать поведение объектов на этапах строительства и разрушения. ] end note

Программа может закончить время жизни любого объекта, повторно используя память, которую занимает объект, или явно вызвав деструктор для объекта типа класса с нетривиальным деструктором. Для объекта типа класса с нетривиальным деструктором программе не требуется явно вызывать деструктор перед повторным использованием или освобождением памяти, занимаемой объектом; однако, если нет явного вызова деструктора или если a delete-expression не используется для освобождения хранилища, деструктор не должен вызываться неявно, и любая программа, которая зависит от побочных эффектов, производимых деструктором, имеет неопределенное поведение.

До того, как время жизни объекта началось, но после того, как хранилище, которое будет занимать объект, было выделено41 или, после того, как время жизни объекта закончилось и до того, как хранилище, которое занимал объект, будет повторно использовано или освобождено, любой указатель, который представляет адрес место хранения, где будет или находился объект, может быть использовано, но только ограниченным образом. Для объекта, находящегося в стадии строительства или разрушения, см [class.cdtor]. В противном случае такой указатель относится к выделенному хранилищу ([basic.stc.dynamic.deallocation]), и использование указателя, как если бы указатель имел тип void*, является четко определенным. Косвенное обращение через такой указатель разрешено, но результирующее значение lvalue можно использовать только ограниченными способами, как описано ниже. Программа имеет неопределенное поведение, если:

  • объект будет или имел тип класса с нетривиальным деструктором, а указатель используется как операнд a delete-expression,

  • указатель используется для доступа к нестатическому члену данных или вызова нестатической функции-члена объекта, или

  • указатель неявно преобразуется ([conv.ptr]) в указатель на виртуальный базовый класс, или

  • указатель используется в качестве операнда static_­cast, за исключением того, когда преобразование является указателем cv void, или указатель , cv void а затем на указатель cv char, cv unsigned charили cv std​::​byte ([cstddef.syn]), или

  • указатель используется как операнд a dynamic_­cast.

[Example:

#include <cstdlib>

struct B {
  virtual void f();
  void mutate();
  virtual ~B();
};

struct D1 : B { void f(); };
struct D2 : B { void f(); };

void B::mutate() {
  new (this) D2;    // reuses storage — ends the lifetime of *this
  f();              // undefined behavior
  ... = this;       // OK, this points to valid memory
}

void g() {
  void* p = std::malloc(sizeof(D1) + sizeof(D2));
  B* pb = new (p) D1;
  pb->mutate();
  *pb;              // OK: pb points to valid memory
  void* q = pb;     // OK: pb points to valid memory
  pb->f();          // undefined behavior, lifetime of *pb has ended
}

end example]

Точно так же до того, как время жизни объекта началось, но после того, как было выделено хранилище, которое будет занимать объект, или после того, как время жизни объекта закончилось и до того, как хранилище, которое занимал объект, будет повторно использовано или освобождено, любое значение glvalue, которое относится к исходный объект можно использовать, но только ограниченным образом. Для объекта, находящегося в стадии строительства или разрушения, см [class.cdtor]. В противном случае такое glvalue относится к выделенному хранилищу ([basic.stc.dynamic.deallocation]), и использование свойств glvalue, которые не зависят от его значения, четко определено. Программа имеет неопределенное поведение, если:

  • glvalue используется для доступа к объекту, или

  • glvalue используется для вызова нестатической функции-члена объекта, или

  • glvalue привязан к ссылке на виртуальный базовый класс ([dcl.init.ref]) или

  • glvalue используется как операнд a dynamic_­cast или как операнд typeid.

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

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и

  • новый объект имеет тот же тип, что и исходный объект (игнорируя cv-квалификаторы верхнего уровня), и

  • тип исходного объекта не квалифицируется как константа, и, если это тип класса, не содержит каких-либо нестатических членов данных, чей тип квалифицируется как константный или ссылочный тип, и

  • исходный объект был most derived object типа a, T а новый объект является наиболее производным объектом типа T (то есть они не являются подобъектами базового класса).

[Example:

struct C {
  int i;
  void f();
  const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();                 // lifetime of *this ends
    new (this) C(other);        // new object of type C created
    f();                        // well-defined
  }
  return *this;
}

C c1;
C c2;
c1 = c2;                        // well-defined
c1.f();                         // well-defined; c1 refers to a new object of type C

end example] [ Note: Если эти условия не выполняются, указатель на новый объект может быть получен из указателя, представляющего адрес его хранилища, путем вызова std​::​launder ([support.dynamic]). ]end note

Если программа завершает время жизни объекта типа T с помощью static, threadили automatic storage duration и если T имеет нетривиальный деструктор,42 программа должна гарантировать, что объект исходного типа занимает то же место хранения, когда имеет место неявный вызов деструктора; в противном случае поведение программы не определено. Это верно, даже если выход из блока произошел с исключением. [Example:

class T { };
struct B {
   ~B();
};

void h() {
   B b;
   new (&b) T;
}                               // undefined behavior at block exit

end example]

Создание нового объекта в хранилище, которое const занимает полный объект со статической, потоковой или автоматической продолжительностью хранения, или в хранилище, которое такой const объект занимал до истечения срока его существования, приводит к неопределенному поведению. [Example:

struct B {
  B();
  ~B();
};

const B b;

void h() {
  b.~B();
  new (const_cast<B*>(&b)) const B;     // undefined behavior
}

end example]

В этом разделе «до» и «после» относятся к отношению «happens before». [ Note: Следовательно, неопределенное поведение возникает, если на объект, который создается в одном потоке, ссылаются из другого потока без адекватной синхронизации. ] end note

Например, перед построением глобального объекта типа класса не POD ([class.cdtor]).

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

6.9 Types [basic.types]

[ Note: [basic.types] и его подпункты налагают требования к реализациям в отношении представления типов. Есть два типа типов: основные типы и составные типы. Типы описания objects, referencesили functions. ]end note

Для любого объекта (кроме базового класса подобъекта) из тривиального Copyable типа T, выполняется ли или нет объекта действительного значения типа T, лежащий в основе байта ([intro.memory]), составляющие объект может быть скопирован в массив char, unsigned charили std​::​byte ([cstddef.syn]) .43 Если содержимое этого массива копируется обратно в объект, объект впоследствии должен сохранить свое исходное значение. [Example:

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized to its original value
std::memcpy(buf, &obj, N);      // between these two calls to std​::​memcpy, obj might be modified
std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type holds its original value

end example]

Для любого тривиально копируемого типа T, если два указателя T указывают на разные T объекты obj1 и obj2, если ни один из них obj1 не obj2 является подобъектом базового класса, если базовые составляющие bytes ([intro.memory]) obj1 копируются в obj2,44 obj2 впоследствии будут иметь то же значение, что и obj1. [Example:

T* t1p;
T* t2p;
    // provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
    // at this point, every subobject of trivially copyable type in *t1p contains
    // the same value as the corresponding subobject in *t2p

end example]

Объект типа представляет собой последовательность объектов , принятых до объекта типа , где равных . Объект объекта - это набор битов, содержащих значение типа . Для тривиально копируемых типов представление значения - это набор битов в представлении объекта, который определяет a , который является одним дискретным элементом набора значений, определяемого реализацией. object representation T N unsigned char T N sizeof(T)value representation Tvalue45

Класс, который был объявлен, но не определен, тип перечисления в определенных контекстах ([dcl.enum]) или массив с неизвестной привязкой или неполным типом элемента - это incompletely-defined object type.46 Неполностью определенные типы объектов и cv void are incomplete types ([basic.fundamental]). Объекты не должны определяться как имеющие неполный тип.

Тип класса (например, «class X») может быть неполным в какой-то момент единицы перевода и завершиться позже; тип «class X» - это один и тот же тип в обеих точках. Объявленный тип объекта массива может быть массивом неполного типа класса и, следовательно, неполным; если тип класса завершается позже в единице трансляции, тип массива становится полным; тип массива в этих двух точках будет одного и того же типа. Объявленный тип объекта массива может быть массивом с неизвестными границами и, следовательно, быть неполным в какой-то момент в единице перевода и завершиться позже; типы массивов в этих двух точках («массив с неизвестной границей T» и «массив из N T») относятся к разным типам. Тип указателя на массив с неизвестной границей или типа, определенного typedef объявлением как массив с неизвестной границей, не может быть завершен. [Example:

class X;                        // X is an incomplete type
extern X* xp;                   // xp is a pointer to an incomplete type
extern int arr[];               // the type of arr is incomplete
typedef int UNKA[];             // UNKA is an incomplete type
UNKA* arrp;                     // arrp is a pointer to an incomplete type
UNKA** arrpp;

void foo() {
  xp++;                         // ill-formed: X is incomplete
  arrp++;                       // ill-formed: incomplete type
  arrpp++;                      // OK: sizeof UNKA* is known
}

struct X { int i; };            // now X is a complete type
int  arr[10];                   // now the type of arr is complete

X x;
void bar() {
  xp = &x;                      // OK; type is “pointer to X  arrp = &arr;                  // ill-formed: different types
  xp++;                         // OK:  X is complete
  arrp++;                       // ill-formed: UNKA can't be completed
}

end example]

[ Note: Правила для объявлений и выражений описывают, в каких контекстах запрещены неполные типы. ] end note

An object type - это тип (возможно, с квалификацией cv), который не является типом функции, не ссылочным типом и не является cv void.

Arithmetic types, типы перечисления, типы указателей, указатели на типы членов ([basic.compound]) std​::​nullptr_­tи cv-qualified версии этих типов вызываются вместе scalar types. Скалярные типы,, POD classesмассивы таких типов и версии этих типов с квалификацией cv называются вместе POD types. Cv-неквалифицированные скалярные типы, trivially copyable class типы, массивы таких типов и версии этих типов с cv-квалификацией называются вместе trivially copyable types. Скалярные типы, trivial class типы, массивы таких типов и версии этих типов с квалификацией cv называются вместе trivial types. Скалярные типы, standard-layout class типы, массивы таких типов и версии этих типов с квалификацией cv называются вместе standard-layout types.

Тип - это, literal type если он:

  • возможно cv-квалификация void; или

  • скалярный тип; или

  • ссылочный тип; или

  • массив буквального типа; или

  • возможно cv-квалифицированный class type , обладающий всеми следующими свойствами:

    • у него есть тривиальный деструктор,

    • это либо a closure type, an aggregate type, либо имеет хотя бы один конструктор constexpr или шаблон конструктора (возможно, inherited из базового класса), который не является конструктором копирования или перемещения,

    • если это объединение, по крайней мере, один из его нестатических элементов данных имеет энергонезависимый литеральный тип, и

    • если это не объединение, все его нестатические элементы данных и базовые классы имеют энергонезависимые литеральные типы.

[ Note: Литеральный тип - это тот, для которого можно создать объект в константном выражении. Это не гарантия того, что можно создать такой объект, и не гарантия того, что любой объект этого типа будет использоваться в константном выражении. ]end note

Два типа cv1 T1 и cv2 T2 являются layout-compatible типами, если T1 и T2 являются одним и тем же типом layout-compatible enumerations, или layout-compatible standard-layout class types.

Используя, например, библиотечные функции ([headers]) std​::​memcpy или std​::​memmove.

Используя, например, библиотечные функции ([headers]) std​::​memcpy или std​::​memmove.

Намерение состоит в том, чтобы модель памяти C ++ была совместима с моделью памяти ISO / IEC 9899 Programming Language C.

Размер и расположение экземпляра не полностью определенного типа объекта неизвестны.

6.9.1 Fundamental types [basic.fundamental]

Объекты, объявленные как символы (char), должны быть достаточно большими, чтобы хранить любой член базового набора символов реализации. Если символ из этого набора хранится в символьном объекте, интегральное значение этого символьного объекта равно значению односимвольной буквальной формы этого символа. Это определяется реализацией, char может ли объект содержать отрицательные значения. Персонажи могут быть объявлены явно или . Обычный , и - это три разных типа, которые вместе называются . A , a и an занимают одинаковый объем памяти и имеют одинаковые значения ; то есть они имеют одно и то же объектное представление. Для узких символьных типов все биты представления объекта участвуют в представлении значения. [ Битовое поле узкого символьного типа, длина которого больше, чем количество бит в представлении объекта этого типа, имеет биты заполнения; см . ] Для беззнаковых узких символьных типов каждый возможный битовый шаблон представления значения представляет отдельное число. Эти требования не действуют для других типов. В любой конкретной реализации простой объект может принимать те же значения, что и a, или a ; какой из них определяется реализацией. Для каждого значения типа в диапазоне от 0 до 255 включительно существует такое значение типа , что результат от до равен , а результат интегрального преобразования от до равен . unsigned signed char signed char unsigned char narrow character types char signed charunsigned char alignment requirementsNote: [class.bit]end note char signed char unsigned char i unsigned char j char integral conversion i char jj unsigned char i

Всего их пять standard signed integer types : « », « », « », « » и « ». В этом списке каждый тип обеспечивает по крайней мере такой же объем памяти, как и предыдущие в списке. Также может быть определено реализацией . Стандартные и расширенные целочисленные типы со знаком называются вместе . Обычные имеют естественный размер, предполагаемый архитектурой среды исполнения ; другие целочисленные типы со знаком предоставляются для особых нужд.signed charshort intintlong intlong long intextended signed integer typessigned integer typesint47

Для каждого из стандартных целочисленных типов со знаком существуют соответствующие (но разные) standard unsigned integer type: « », « », « », « » и « », каждый из которых занимает такой же объем памяти и имеет то же самое, что и соответствующий знаковый целочисленный тип ; то есть каждый знаковый целочисленный тип имеет то же представление объекта, что и соответствующий ему беззнаковый целочисленный тип. Аналогично, для каждого из расширенных целочисленных типов со знаком существует соответствующий с одинаковым объемом памяти и требованиями к выравниванию. Стандартные и расширенные целочисленные типы без знака называются вместе . Диапазон неотрицательных значений целочисленного типа со знаком является поддиапазоном соответствующего целочисленного типа без знака, представление одного и того же значения в каждом из двух типов является одинаковым, и представление значения каждого соответствующего типа со знаком / без знака должно быть таким же. Стандартные целочисленные типы со знаком и стандартные целочисленные типы без знака вместе называются , а расширенные целочисленные типы со знаком и расширенные целочисленные типы без знака вместе называются . Целочисленные типы со знаком и без знака должны удовлетворять ограничениям, приведенным в стандарте C, раздел 5.2.4.2.1.unsigned charunsigned short intunsigned intunsigned long intunsigned long long int alignment requirements 48extended unsigned integer type unsigned integer types standard integer typesextended integer types

Целые числа без знака должны подчиняться законам арифметики по модулю, 2n где n - количество бит в представлении значения этого конкретного размера целого числа.49

Тип wchar_­t - это отдельный тип, значения которого могут представлять различные коды для всех членов самого большого расширенного набора символов, указанного среди поддерживаемых locales. Тип wchar_­t должен иметь такой же размер, подписи и, alignment requirements как и один из других интегральных типов, называться своим underlying type. Типы char16_­t и char32_­t обозначают отдельные типы с тем же размером, подписью и выравниванием, что uint_­least16_­t и и uint_­least32_­t, соответственно, в <cstdint>, называемые базовыми типами.

Значения типа bool : либо, true либо false.50 [ Note: Там нет signed, unsigned, shortили long bool типов или значения. ] Значения типа участвуют в . end notebool integral promotions

Типы bool, char, char16_­t, char32_­t, wchar_­t, и подписанные и неподписанные целые типы собирательно называются integral типами.51 Синонимом интегрального типа является . Представления целочисленных типов должны определять значения с использованием чистой двоичной системы счисления. [ Этот международный стандарт допускает представление двух дополнений, единиц дополнения и знаковых величин для целочисленных типов. ]integer type52Example: end example

Есть три floating-point типа: float, double, и long double. Тип double обеспечивает по крайней мере такую ​​же точность float, а тип long double обеспечивает по крайней мере такую ​​же точность, как double. Набор значений типа float - это подмножество набора значений типа double; набор значений типа double - это подмножество набора значений типа long double. Представление значений типов с плавающей запятой определяется реализацией. [ Note: Настоящий международный стандарт не предъявляет требований к точности операций с плавающей запятой; см. также [support.limits]. ] Целые и плавающие типы вместе называются типами. Специализации шаблона стандартной библиотеки должны определять максимальные и минимальные значения каждого арифметического типа для реализации.end note arithmetic std​::​numeric_­limits

Тип cv void - это неполный тип, который не может быть завершен; такой тип имеет пустой набор значений. Он используется в качестве возвращаемого типа для функций, не возвращающих значение. Любое выражение можно explicitly converted набрать cv void. Выражение типа cv void должно быть использовано только в качестве expression statement, в качестве операнда comma expression, в качестве второго или третьего операнда ?: ([expr.cond]), как операнд typeid, noexceptили decltype, как выражение в return statement для функции с типом возвращаемого cv void, или как операнд явного преобразования в тип cv void.

Значение типа std​::​nullptr_­t - это null pointer constant. Такие значения участвуют в преобразовании указателя и указателя на член ([conv.ptr], [conv.mem]). sizeof(std​::​nullptr_­t) будет равно sizeof(void*).

[ Note: Даже если реализация определяет два или более базовых типа, которые имеют одинаковое представление значения, они, тем не менее, являются разными типами. ]end note

int также должен быть достаточно большим, чтобы содержать любое значение в диапазоне [INT_­MIN, INT_­MAX], как определено в заголовке <climits>.

См. [dcl.type.simple] Соответствие между типами и последовательностями их type-specifiers обозначающих.

Это означает, что беззнаковая арифметика не переполняется, потому что результат, который не может быть представлен результирующим целочисленным типом без знака, уменьшается по модулю числа, которое на единицу больше наибольшего значения, которое может быть представлено результирующим целочисленным типом без знака.

Использование bool значения способами, описанными в этом международном стандарте как «неопределенное», например, путем проверки значения неинициализированного автоматического объекта, может заставить его вести себя так, как если бы оно ни было, true ни false.

Следовательно, enumerations не являются цельными; однако перечисления можно преобразовать в целочисленные типы, как указано в [conv.prom].

Позиционное представление для целых чисел, использующее двоичные цифры 0 и 1, в котором значения, представленные последовательными битами, являются аддитивными, начинаются с 1 и умножаются на последовательную интегральную степень 2, за исключением, возможно, бита с самой высокой позицией. (Адаптировано из American National Dictionary for Information Processing Systems.)

6.9.2 Compound types [basic.compound]

Составные типы могут быть созданы следующими способами:

  • arrays объектов данного типа [dcl.array],;

  • functions, которые имеют параметры заданных типов и возвращают, void или ссылки, или объекты заданного типа [dcl.fct],;

  • pointers в cv void или объекты или функции (включая статические члены классов) данного типа [dcl.ptr];

  • references к объектам или функциям данного типа [dcl.ref]. Есть два типа ссылок:

  • classes содержащий последовательность объектов различных типов (Clause [class]), набор типов, перечислений и функций для управления этими объектами ([class.mfct]) и набор ограничений на доступ к этим объектам (Clause [class.access]);

  • unions, Которые являются классы , способные содержать объекты разных типов в разные моменты времени, [class.union];

  • enumerations, которые содержат набор именованных постоянных значений. Каждое отдельное перечисление представляет собой различное enumerated type, [dcl.enum];

  • pointers to non-static class members,53 Которые идентифицируют пользователей данного типа в пределах объектов данного класса, [dcl.mptr].

Эти методы построения типов можно применять рекурсивно; ограничения указаны в [dcl.ptr], [dcl.array], [dcl.fct], и [dcl.ref]. Создание типа, в котором количество байтов в его объектном представлении превышает максимальное значение, представленное в type std​::​size_­t ([support.types]), является плохо сформированным.

Тип указателя cv void или указателя на тип объекта называется object pointer type. [ Note: Указатель на void не имеет типа указатель на объект, потому что void не является типом объекта. ] Тип указателя, который может обозначать функцию, называется a . Указатель на объекты типа называется «указателем на ». [ Указатель на объект типа называется «указателем на », а указатель на объект класса называется «указателем на ». ] За исключением указателей на статические члены, текст, относящийся к «указателям», не применяется к указателям на члены. Указатели на неполные типы разрешены, хотя есть ограничения на то, что с ними можно делать ( ). Каждое значение типа указателя является одним из следующих: end note function pointer type T TExample: int int X X end example[basic.align]

Значение типа указателя, которое является указателем на конец или после конца объекта represents the address первого байта в памяти ([intro.memory]), занятого объектом,54 или первого байта в памяти после конца памяти, занятой объектом, соответственно. [ Note: Указатель за концом объекта ([expr.add]) не считается указывающим на несвязанный объект типа объекта, который может быть расположен по этому адресу. Значение указателя становится недействительным, когда память, которую он обозначает, достигает конца срока хранения; см [basic.stc]. ] Для целей и сравнения ( , ), указатель за конец последнего элемента массива из элементов считаются эквивалентным указателем на гипотетический элемент . Представление значений типов указателей определяется реализацией. Указатели на типы, совместимые с макетом, должны иметь одинаковое представление значения и . [ Указатели не должны иметь специального представления, но их диапазон допустимых значений ограничен расширенным требованием выравнивания. ]end note pointer arithmetic[expr.rel] [expr.eq] x n x[n] alignment requirementsNote: over-aligned types end note

Два объекта a и b являются, pointer-interconvertible если:

  • это один и тот же объект, или

  • один - объект объединения стандартного макета, а другой - нестатический член данных этого объекта ([class.union]), или

  • один является объектом класса стандартного макета, а другой - первым нестатическим членом данных этого объекта, или, если объект не имеет нестатических членов данных, первым подобъектом базового класса этого объекта ([class.mem]) или

  • существует такой объект c , что a и c являются взаимопреобразуемыми по указателям, и c и b являются взаимопреобразуемыми по указателям.

Если два объекта взаимопреобразуемы по указателю, то они имеют одинаковый адрес, и можно получить указатель на один из указателя на другой через a reinterpret_­cast. [ Note: Объект массива и его первый элемент не являются взаимопреобразуемыми по указателям, даже если они имеют одинаковый адрес. ]end note

Указатель на cv-qualified или cv-unqualified void может использоваться для указания на объекты неизвестного типа. Такой указатель должен содержать любой указатель на объект. Объект типа cv void* должен иметь те же требования к представлению и выравниванию, что и cv char*.

Статические члены класса - это объекты или функции, а указатели на них - обычные указатели на объекты или функции.

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

6.9.3 CV-qualifiers [basic.type.qualifier]

Тип, упомянутый в [basic.fundamental] и [basic.compound] - это cv-unqualified type. Каждый тип, который является cv-неквалифицированным полным или неполным типом объекта или is void ([basic.types]), имеет три соответствующих cv-квалифицированных версии своего типа: const-qualified версия, volatile-qualified версия и const-volatile-qualified версия. Тип объекта ([intro.object]) включает в себя cv-qualifiers указано в decl-specifier-seq, declarator, type-id, или new-type-idпри создании объекта.

  • A const object - объект типа const T или неизменяемый подобъект такого объекта.

  • A volatile object - это объект типа volatile T, подобъект такого объекта или изменяемый подобъект константного изменчивого объекта.

  • A const volatile object - это объект типа const volatile T, неизменяемый подобъект такого объекта, константный подобъект изменчивого объекта или неизменяемый изменчивый подобъект константного объекта.

Варианты типа cv-qualified или cv-unqualified являются отдельными типами; однако они должны иметь одинаковое представление и alignment requirements.55

A compound type не квалифицируется cv-квалификаторами (если есть) типов, из которых он составлен. Любые cv-квалификаторы, применяемые к типу массива, влияют на тип элемента массива ([dcl.array]).

См [dcl.fct] и [class.this] относительно типов функций , которые имеют cv-qualifiers.

В cv-квалификаторах существует частичный порядок, поэтому можно сказать, что один тип отличается more cv-qualified от другого. В таблице 10 показаны отношения, составляющие этот порядок.

Таблица 10 - Отношения по const и volatile
no cv-qualifier < const
no cv-qualifier < volatile
no cv-qualifier < const volatile
const < const volatile
volatile < const volatile

В этом международном стандарте нотация cv (или cv1, cv2и т. Д.), Используемая в описании типов, представляет произвольный набор cv-квалификаторов, т. Е. Один из {const}, {volatile}, {const, volatile} или пустой набор. Для типа cv T, то top-level cv-qualifiers из этого типа являются те , которые обозначены cv. [ Example: Тип, соответствующий типу type-id const int& , не имеет квалификаторов CV верхнего уровня. Тип, соответствующий type-id volatile int * const тегу, имеет квалификатор cv верхнего уровня const. Для типа класса тип C, соответствующий классу , type-id void (C​::​* volatile)(int) const имеет квалификатор cv верхнего уровня volatile. ]end example

Cv-квалификаторы, применяемые к типу массива, присоединяются к базовому типу элемента, поэтому обозначение «cv T», где T является типом массива, относится к массиву, элементы которого квалифицируются таким образом. Тип массива, элементы которого имеют cv-квалификацию, также считается имеющим такую ​​же cv-квалификацию, что и его элементы. [Example:

typedef char CA[5];
typedef const char CC;
CC arr1[5] = { 0 };
const CA arr2 = { 0 };

Типом обоих arr1 и arr2 является «массив из 5 const char», а тип массива считается квалифицированным константой. ]end example

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

6.10 Lvalues and rvalues [basic.lval]

Выражения классифицируются в соответствии с таксономией на рисунке [fig:categories].

categories expression expression expression glvalue glvalue glvalue expression->glvalue expression->glvalue rvalue rvalue rvalue expression->rvalue expression->rvalue lvalue lvalue lvalue glvalue->lvalue glvalue->lvalue xvalue xvalue xvalue glvalue->xvalue glvalue->xvalue rvalue->xvalue rvalue->xvalue prvalue prvalue prvalue rvalue->prvalue rvalue->prvalue
Рисунок 1 - Таксономия категории выражений
  • A glvalue - это выражение, оценка которого определяет идентичность объекта, битового поля или функции.

  • A prvalue - это выражение, оценка которого инициализирует объект или битовое поле или вычисляет значение операнда оператора в соответствии с контекстом, в котором он появляется.

  • An xvalue - это значение glvalue, которое обозначает объект или битовое поле, ресурсы которого можно использовать повторно (обычно потому, что срок его существования приближается к концу). [ Example: Определенные виды выражений, включающие rvalue references выходные значения x, такие как вызов функции, возвращаемый тип которой является ссылкой rvalue, или приведением к ссылочному типу rvalue. ] end example

  • An lvalue - это значение glvalue, которое не является значением x.

  • An rvalue - это prvalue или xvalue.

[ Note: Исторически lvalues ​​и rvalues ​​назывались так, потому что они могли появляться слева и справа от присваивания (хотя это больше не так); glvalues ​​- это «обобщенные» l-значения, prvalue - «чистые» r-значения, а xvalue - «eXpiring» l-значения. Несмотря на названия, эти термины классифицируют выражения, а не значения. ] Каждое выражение принадлежит ровно к одной из фундаментальных классификаций в этой таксономии: lvalue, xvalue или prvalue. Это свойство выражения называется его . [ Обсуждение каждого встроенного оператора в разделе указывает категорию значения, которое он дает, и категории значений операндов, которые он ожидает. Например, встроенные операторы присваивания ожидают, что левый операнд является lvalue, а правый операнд - prvalue, и в результате выдают lvalue. Определяемые пользователем операторы - это функции, а категории ожидаемых и получаемых ими значений определяются их параметрами и типами возвращаемых значений. ]end note value categoryNote: [expr] end note

result Из prvalue этого значения , которое экспрессирующих магазинов в его контекст. Иногда говорят, что prvalue, результатом которого является значение, V имеет значение или дает имя V. result object Из prvalue является объектом инициализируется prvalue; prvalue, которое используется для вычисления значения операнда оператора или которое имеет тип, cv void не имеет объекта результата. [ Note: За исключением случаев, когда prvalue является операндом a decltype-specifier, prvalue класса или типа массива всегда имеет объект результата. Для отброшенного значения prvalue материализуется временный объект; см. пункт [expr]. ] Значение glvalue - это сущность, обозначенная выражением.end note result

[ Note: Каждый раз, когда glvalue появляется в контексте, где ожидается prvalue, glvalue конвертируется в prvalue; см [conv.lval], [conv.array]и [conv.func]. Попытка привязать ссылку rvalue к lvalue не является таким контекстом; см [dcl.init.ref]. ] [ Нет битовых полей prvalue; если битовое поле есть , создается prvalue типа битового поля, которое затем может быть . ]end noteNote: converted to a prvalue promotedend note

[ Note: Каждый раз, когда prvalue появляется в контексте, где ожидается glvalue, prvalue преобразуется в xvalue; см [conv.rval]. ]end note

Обсуждение инициализации ссылок в [dcl.init.ref] и временных значений в [class.temporary] указывает на поведение lvalue и rvalues ​​в других значимых контекстах.

Если не указано иное ([expr.call]), prvalue всегда должен иметь полный тип или void тип. У glvalue не должно быть типа cv void. [ Note: Значение glvalue может иметь полный или неполныйvoid тип, отличный от типа. Класс и массив prvalue могут иметь типы с квалификацией cv; у других prvalues ​​всегда есть типы cv-unqualified. См. Пункт [expr]. ]end note

Значение lvalue имеет значение, modifiable если его тип не квалифицируется как const или не является типом функции. [ Note: Программа , которая пытается изменить объект с помощью выражения nonmodifiable именующего или через выражение RValue плохо сформированная ([expr.ass], [expr.post.incr], [expr.pre.incr]). ]end note

Если программа пытается получить доступ к сохраненному значению объекта с помощью glvalue, отличного от одного из следующих типов, поведение не определено:56

  • динамический тип объекта,

  • cv-квалифицированная версия динамического типа объекта,

  • тип similar к динамическому типу объекта,

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

  • тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа объекта,

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

  • тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,

  • a char, unsigned charили std​::​byte введите.

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

6.11 Alignment [basic.align]

Типы объектов имеют alignment requirements ([basic.fundamental], [basic.compound]), которые накладывают ограничения на адреса, по которым может быть размещен объект этого типа. An alignment - это определяемое реализацией целочисленное значение, представляющее количество байтов между последовательными адресами, по которым может быть размещен данный объект. Тип объекта налагает требование выравнивания на каждый объект этого типа; более строгое выравнивание можно запросить с помощью alignment specifier.

A fundamental alignment представлен выравниванием, меньшим или равным наибольшему выравниванию, поддерживаемому реализацией во всех контекстах, которое равно alignof(std​::​max_­align_­t) ([support.types]). Выравнивание, необходимое для типа, может быть различным, когда он используется как тип полного объекта и когда он используется как тип подобъекта. [Example:

struct B { long double d; };
struct D : virtual B { char c; };

Когда D это тип полного объекта, у него будет подобъект типа B, поэтому он должен быть соответствующим образом выровнен для объекта long double. Если D подобъект отображается как подобъект другого объекта, который также имеет B виртуальный базовый класс, B подобъект может быть частью другого подобъекта, что снижает требования к выравниванию D подобъекта. ] Результат оператора отражает требование выравнивания типа в случае полного объекта.end example alignof

extended alignment Представлен в большей согласованности , чем alignof(std​::​max_­align_­t). Это определяется реализацией, поддерживаются ли какие-либо расширенные выравнивания и контексты, в которых они поддерживаются ([dcl.align]). Тип, требующий расширенного выравнивания, - это over-aligned type. [ Note: Каждый сверхвыровненный тип является или содержит тип класса, к которому применяется расширенное выравнивание (возможно, через нестатический член данных). ] A представлен выравниванием больше чем .end note new-extended alignment _­_­STDCPP_­DEFAULT_­NEW_­ALIGNMENT_­_­

Выравнивания представлены как значения типа std​::​size_­t. Допустимые выравнивания включают только те значения, которые возвращаются alignof выражением для основных типов, плюс дополнительный набор значений, определяемый реализацией, который может быть пустым. Каждое значение выравнивания должно быть неотрицательной интегральной степенью двойки.

Выравнивания имеют порядок от weaker до stronger или stricter выравнивания. Более строгие выравнивания имеют более высокие значения выравнивания. Адрес, который удовлетворяет требованию выравнивания, также удовлетворяет любому более слабому действительному требованию выравнивания.

Требование выравнивания полного типа можно запросить с помощью файла alignof expression. Кроме того, они narrow character types должны иметь самые слабые требования к выравниванию. [ Note: Это позволяет использовать узкие типы символов в качестве базового типа для выровненной области памяти ([dcl.align]). ]end note

Сравнение выравниваний имеет смысл и дает очевидные результаты:

  • Два выравнивания равны, если их числовые значения равны.

  • Два выравнивания различаются, если их числовые значения не равны.

  • Когда выравнивание больше другого, это означает более строгое выравнивание.

[ Note: Среда выполнения pointer alignment function может использоваться для получения выровненного указателя в буфере; шаблоны выровненного хранилища в библиотеке ([meta.trans.other]) можно использовать для получения выровненного хранилища. ]end note

Если запрос на конкретное расширенное выравнивание в конкретном контексте не поддерживается реализацией, программа плохо сформирована.