8 Expressions [expr]

[ Note: Пункт [expr] определяет синтаксис, порядок оценки и значение выражений. 62 Выражение - это последовательность операторов и операндов, определяющая вычисление. Выражение может привести к значению и вызвать побочные эффекты. ]end note

[ Note: Операторы могут быть перегружены, т. Е. Придавать значение при применении к выражениям class типа или enumeration типа. Использование перегруженных операторов преобразуется в вызовы функций, как описано в [over.oper]. Перегруженные операторы подчиняются правилам синтаксиса и порядка оценки, указанным в разделе [expr], но требования к типу операнда и категории значения заменяются правилами для вызова функций. Отношения между операторами, например ++a значения a+=1, не гарантируются overloaded operators. ]end note

Предложение [expr] определяет эффекты операторов при применении к типам, для которых они не были перегружены. Перегрузка оператора не должна изменять правила для built-in operatorsоператоров, то есть для операторов, применяемых к типам, для которых они определены настоящим стандартом. Однако эти встроенные операторы участвуют в разрешении перегрузки, и как часть этого процесса будут рассматриваться определяемые пользователем преобразования там, где это необходимо, для преобразования операндов в типы, подходящие для встроенного оператора. Если выбран встроенный оператор, такие преобразования будут применены к операндам до того, как операция будет рассмотрена далее в соответствии с правилами в разделе [expr]; см. [over.match.oper], [over.built].

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

Если выражение изначально имеет тип «ссылка на T» ( [dcl.ref], [dcl.init.ref]), тип корректируется T до любого дальнейшего анализа. Выражение обозначает объект или функцию, обозначенную ссылкой, а выражение представляет собой lvalue или xvalue, в зависимости от выражения. [ Note: До того, как время жизни ссылки началось или после того, как оно закончилось, поведение не определено (см. [basic.life]). ]end note

Если prvalue изначально имеет тип « cv T», где T cv-неквалифицированный неклассовый , не-массивный тип, тип выражения корректируется T до любого дальнейшего анализа.

[ Note: Выражение является значением x, если оно:

  • результат неявного или явного вызова функции, возвращаемый тип которой является ссылкой rvalue на тип объекта,

  • приведение к ссылке rvalue на тип объекта,

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

  • .* выражение указатель на член , в котором первый операнд является xvalue , а второй операнд является указателем элемента данных.

В общем, действие этого правила состоит в том, что именованные ссылки rvalue обрабатываются как lvalue, а безымянные ссылки rvalue на объекты обрабатываются как xvalue; Ссылки rvalue на функции обрабатываются как lvalue независимо от того, имеют ли они имена. ]end note

[Example:

struct A {
  int m;
};
A&& operator+(A, A);
A&& f();

A a;
A&& ar = static_cast<A&&>(a);

Выражения f(), f().m, static_­cast<A&&>(a)и a + a являются xvalues. Выражение ar - это lvalue. ]end example

В некоторых контекстах, unevaluated operands появляются ( [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple]). Неоцененный операнд не оценивается. [ Note: В неоцененном операнде нестатический член класса может иметь имя ( [expr.prim]), а именование объектов или функций само по себе не требует предоставления определения ( [basic.def.odr]). Неоцененный операнд считается полным выражением ( [intro.execution]). ]end note

Всякий раз , когда выражение glvalue появляется в качестве операнда оператора, ожидающей prvalue для этого операнда, lvalue-to-rvalue, array-to-pointerили function-to-pointer стандартные преобразования применяются для преобразования выражения в prvalue. [ Note: Поскольку cv-квалификаторы удаляются из типа выражения неклассового типа, когда выражение преобразуется в prvalue, выражение lvalue типа const int может, например, использоваться там, где требуется выражение prvalue типа int . ]end note

Каждый раз, когда выражение prvalue появляется как операнд оператора, который ожидает glvalue для этого операнда, temporary materialization conversion применяется для преобразования выражения в xvalue.

Многие бинарные операторы, которые ожидают операндов арифметического или перечислительного типа, вызывают преобразования и выдают типы результатов аналогичным образом. Цель состоит в том, чтобы получить общий тип, который также является типом результата. Этот паттерн называется паттерном usual arithmetic conversions, который определяется следующим образом:

  • Если какой-либо из операндов имеет область видимости enumeration type, преобразования не выполняются; если другой операнд не имеет того же типа, выражение неправильно сформировано.

  • Если один из операндов имеет тип long double, другой должен быть преобразован в long double.

  • В противном случае, если один из операндов равен double, другой должен быть преобразован в double.

  • В противном случае, если один из операндов равен float, другой должен быть преобразован в float.

  • В противном случае integral promotions должно выполняться для обоих операндов. 63 Затем к продвигаемым операндам применяются следующие правила:

    • Если оба операнда имеют один и тот же тип, дальнейшее преобразование не требуется.

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

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

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

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

В некоторых контекстах выражение появляется только из-за его побочных эффектов. Такое выражение называется a discarded-value expression. В array-to-pointer и function-to-pointer стандартные преобразования не применяются. lvalue-to-rvalue conversion Применяются тогда и только тогда , когда выражение является glvalue летучего-квалифицированного типа и является одним из следующих:

[ Note: Использование перегруженного оператора вызывает вызов функции; вышеупомянутое относится только к операторам со встроенным значением. ] Если после этого необязательного преобразования выражение является значением prvalue, применяется. [ Если выражение является lvalue типа класса, оно должно иметь конструктор изменчивой копии для инициализации временного объекта, являющегося результатом преобразования lvalue-to-rvalue. ] Выражение glvalue вычисляется, а его значение отбрасывается.end note temporary materialization conversion Note: end note

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

cv-combined type Двух типов T1 и T2 тип T3 похож на T1 которой cv-qualification signature находится:

  • для каждого i>0, cv3i является объединением cv1i и cv2i;

  • если результат cv3i отличается от cv1i или cv2i, то const добавляется к каждому cv3k for 0<k<i.

[ Note: Учитывая схожие типы T1 и T2, эта конструкция гарантирует, что оба могут быть преобразованы в T3. ] end note

composite pointer type Из двух операндов p1 и p2 имеющие типов T1 и T2, соответственно, где , по меньшей мере , один указатель или указатель на тип или член std​::​nullptr_­t, является:

  • если оба p1 и p2 являются нулевыми константами указателя, std​::​nullptr_­t;

  • если либо p1 или p2 является константой нулевого указателя, T2 либо T1, соответственно;

  • если T1 или T2 является «указателем на cv1 void», а другой тип - «указателем на cv2 T», где T является типом объекта или void«указателем на cv12 void», где cv12 является объединением cv1 и cv2;

  • если T1 или T2 является «указателем на noexcept функцию», а другой тип - «указателем на функцию», где типы функций в остальном такие же, «указатель на функцию»;

  • если T1 это «указатель на cv1 C1» и T2 является «указатель cv2 C2», где C1 находится reference-related на C2 или C2 является ссылка , связанных с C1, в сочетании CV-типа T1 и T2 или резюме комбинированного типа T2 и T1, соответственно;

  • if T1 - «указатель на член C1 типа cv1 U1» и T2 «указатель на член C2 типа cv2 U2», где C1 связан со ссылкой C2 или C2 относится reference-related к C1типу cv-comb T2 и / T1 или типу cv -comdated T1 и T2, соответственно;

  • если T1 и T2 являются similar types, cv-комбинированный тип T1 и T2;

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

[Example:

typedef void *p;
typedef const int *q;
typedef int **pi;
typedef const int **pci;

Тип составного указателя p и q - «указатель на const void»; тип составного указателя pi и pci - «указатель на const указатель на const int». ]end example

Приоритет операторов прямо не указывается, но может быть получен из синтаксиса.

Как следствие, операнды типа bool, char16_­t, char32_­t, wchar_­t, или перечислимый типа преобразуются к некоторому интегральному типу.

Операторы приведения и присваивания должны по-прежнему выполнять свои конкретные преобразования, как описано в [expr.cast], [expr.static.cast] и [expr.ass].

8.1 Primary expressions [expr.prim]

primary-expression:
	literal
	this
	( expression )
	id-expression
	lambda-expression
	fold-expression

8.1.1 Literals [expr.prim.literal]

A - это первичное выражение. Его тип зависит от его . Строковый литерал - это lvalue; все остальные литералы являются prvalue.literal form

8.1.2 This [expr.prim.this]

Ключевое слово this именует указатель на объект, для которого вызывается нестатическая функция-член или [class.mem]оценивается инициализатор ( ) нестатического элемента данных .

Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение this представляет собой prvalue типа «указатель на cv-qualifier-seq X» между необязательным cv-qualifier-seqи концом function-definition, member-declaratorили declarator. Он не должен появляться перед необязательным cv-qualifier-seqи не должен появляться в объявлении статической функции-члена (хотя ее тип и категория значения определены в статической функции-члене, поскольку они находятся в нестатической функции-члене). [ Note: Это связано с тем, что сопоставление деклараций не происходит до тех пор, пока не будет известен полный декларатор. ] В отличие от объектного выражения в других контекстах, не требуется, чтобы он был полного типа для целей вне тела функции-члена. [ Видны только члены класса, объявленные до объявления. ] [ end note *this class member access Note: end noteExample:

struct A {
  char g();
  template<class T> auto f(T t) -> decltype(t + g())
    { return t + g(); }
};
template auto A::f(int t) -> decltype(t + g());

end example]

В противном случае, если a member-declaratorобъявляет нестатический data member класс X, выражение this является prvalue типа «указатель на X» внутри необязательного default member initializer. Он не должен появляться где-либо еще в member-declarator.

Выражение this не должно появляться ни в каком другом контексте. [Example:

class Outer {
  int a[sizeof(*this)];               // error: not inside a member function
  unsigned int sz = sizeof(*this);    // OK: in default member initializer

  void f() {
    int b[sizeof(*this)];             // OK

    struct Inner {
      int c[sizeof(*this)];           // error: not inside a member function of Inner
    };
  }
};

end example]

8.1.3 Parentheses [expr.prim.paren]

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

8.1.4 Names [expr.prim.id]

id-expression:
	unqualified-id
	qualified-id

An id-expression- это ограниченная форма primary-expression. [ Может появиться после . ]Note: id-expression. and -> operatorsend note

Можно id-expressionиспользовать только нестатический член данных или нестатическую функцию-член класса:

  • как часть, class member access в которой выражение объекта относится к классу члена 65 или классу, производному от этого класса, или

  • чтобы сформировать указатель на member ( [expr.unary.op]), или

  • если это id-expressionобозначает нестатический член данных и появляется в неоцененном операнде. [Example:

    struct S {
      int m;
    };
    int i = sizeof(S::m);           // OK
    int j = sizeof(S::m + 42);      // OK
    

    end example]

Это также применимо, когда объектное выражение является неявным (*this) ( [class.mfct.non-static]).

8.1.4.1 Unqualified names [expr.prim.id.unqual]

unqualified-id:
	identifier
	operator-function-id
	conversion-function-id
	literal-operator-id
	~ class-name
	~ decltype-specifier
	template-id

identifierПриведено при id-expressionусловии , что было соответствующим образом объявлено (пункт [dcl.dcl]). [ Note: Для получения operator-function-idsсм [over.oper]; для conversion-function-ids, см [class.conv.fct]; для literal-operator-ids, см [over.literal]; для template-ids, см [temp.names]. A class-name или с decltype-specifier префиксом ~ означает деструктор; см [class.dtor]. В определении нестатической функции-члена, identifierимя нестатического члена преобразуется в выражение доступа к члену класса ( [class.mfct.non-static]). ] Тип выражения - это тип . Результатом является сущность, обозначенная идентификатором. Выражение является lvalue, если сущность является функцией, переменной или членом данных, и prvalue в противном случае; это битовое поле, если идентификатор обозначает битовое поле ( ).end noteidentifier[dcl.struct.bind]

8.1.4.2 Qualified names [expr.prim.id.qual]

qualified-id:
	nested-name-specifier templateopt unqualified-id

nested-name-specifier:
	::
	type-name ::
	namespace-name ::
	decltype-specifier ::
	nested-name-specifier identifier ::
	nested-name-specifier templateopt simple-template-id ::

Тип, обозначенный a decltype-specifierв a, nested-name-specifierдолжен быть классом или перечислимым типом.

A nested-name-specifier, обозначающий класс, за которым может следовать ключевое слово template ( [temp.names]), а затем имя члена этого класса ( [class.mem]) или одного из его base classes, - это a ; описывает поиск по имени для членов класса, которые появляются в . Результат - член. Тип результата - это тип члена. Результатом является lvalue, если член является статической функцией-членом или членом данных, и prvalue в противном случае. [К члену класса можно обратиться с помощью a в любой точке его потенциальной области видимости ( ). ] Где используется, оба должны относиться к одному и тому же классу; это обозначение именует . Форма также обозначает деструктор, но она не должна использоваться в качестве элемента в a . [ Имя класса - это ( ). ]qualified-id [class.qual] qualified-idsNote: qualified-id[basic.scope.class]end noteclass-name ​::​~ class-nameclass-names destructor ~ decltype-specifierunqualified-idqualified-idNote: typedef-nameclass-name[class.name]end note

В nested-name-specifier ​::​ именах глобального пространства имен. A, nested-name-specifierкоторый называет пространство имен ( [basic.namespace]), за которым необязательно следует ключевое слово template ( [temp.names]), а затем следует имя члена этого пространства имен (или имя члена пространства имен, сделанное видимым с помощью a using-directive), является a ; описывает поиск по именам для членов пространства имен, которые появляются в . Результат - член. Тип результата - это тип члена. Результатом является lvalue, если член является функцией или переменной, и prvalue в противном случае.qualified-id [namespace.qual] qualified-ids

A, nested-name-specifierкоторый обозначает enumeration, за которым следует имя перечислителя этого перечисления, является a, qualified-id который ссылается на перечислитель. Результат - счетчик. Тип результата - это тип перечисления. Результат - prvalue.

В a qualified-id, если unqualified-id является a conversion-function-id, он conversion-type-id должен обозначать один и тот же тип как в контексте, в котором qualified-idпроисходит целое, так и в контексте класса, обозначенного nested-name-specifier.

8.1.5 Lambda expressions [expr.prim.lambda]

lambda-expression:
	lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
	[ lambda-captureopt ]
lambda-declarator:
	( parameter-declaration-clause ) decl-specifier-seqopt
	noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

Лямбда-выражения обеспечивают краткий способ создания простых функциональных объектов. [Example:

#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned N) {
  std::sort(x, x + N, [](float a, float b) { return std::abs(a) < std::abs(b); });
}

end example]

A lambda-expression- это prvalue, объект результата которого называется closure object. A lambda-expressionне должен появляться в unevaluated operand, в template-argument, в alias-declaration, в объявлении typedef или в объявлении функции или шаблона функции вне ее тела функции и аргументов по умолчанию. [ Note: Цель состоит в том, чтобы предотвратить появление лямбда-выражений в подписи. ] [ Замыкающий объект ведет себя как объект . ]end noteNote: function objectend note

В decl-specifier-seqчасти lambda-declaratorкаждый decl-specifier должен быть mutable либо constexpr.

Если a lambda-expressionне включает a lambda-declarator, это как если бы lambda-declaratorбыло (). Тип возврата лямбда - это auto, который заменяется типом, указанным в trailing-return-typeif, предоставленном и / или выведенном из return операторов, как описано в [dcl.spec.auto]. [Example:

auto x1 = [](int i){ return i; };     // OK: return type is int
auto x2 = []{ return { 1, 2 }; };     // error: deducing return type from braced-init-list
int j;
auto x3 = []()->auto&& { return j; }; // OK: return type is int&

end example]

8.1.5.1 Closure types [expr.prim.lambda.closure]

Тип a lambda-expression(который также является типом закрывающего объекта) - это уникальный безымянный тип класса без объединения, называемый the closure type, свойства которого описаны ниже.

Тип закрытия объявляется в области наименьшего блока, области класса или области имен, которая содержит соответствующий lambda-expression. [ Note: Это определяет набор пространств имен и классов, связанных с типом замыкания ( [basic.lookup.argdep]). Типы параметров lambda-declaratorне влияют на эти связанные пространства имен и классы. ] Тип замыкания не является агрегатным типом ( ). Реализация может определять тип закрытия иначе, чем описано ниже, при условии, что это не изменяет наблюдаемое поведение программы, кроме как путем изменения: end note[dcl.init.aggr]

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

Тип закрытия для неуниверсального типа lambda-expressionимеет общедоступную встроенную строку function call operator , параметры и возвращаемый тип которой описываются lambda-expressionсимволами parameter-declaration-clauseи trailing-return-type соответственно. Для общей лямбды тип замыкания имеет общедоступный встроенный оператор вызова функции member template , который template-parameter-listсостоит из одного придуманного типа template-parameterдля каждого вхождения auto в лямбда parameter-declaration-clauseв порядке появления. Изобретенный тип template-parameter- это пакет параметров, если соответствующий parameter-declarationобъявляет функцию параметра pack ( [dcl.fct]). Тип возвращаемого значения и параметры функции шаблона оператора вызова функции являются производными от lambda-expression's trailing-return-typeи parameter-declaration-clauseпутем замены каждого вхождения auto в decl-specifiers the parameter-declaration-clauseна имя соответствующего изобретенного template-parameter. [Example:

auto glambda = [](auto a, auto&& b) { return a < b; };
bool b = glambda(3, 3.14);                             // OK

auto vglambda = [](auto printer) {
  return [=](auto&& ... ts) {                          // OK: ts is a function parameter pack
    printer(std::forward<decltype(ts)>(ts)...);

    return [=]() {
      printer(ts ...);
    };
  };
};
auto p = vglambda( [](auto v1, auto v2, auto v3)
                   { std::cout << v1 << v2 << v3; } );
auto q = p(1, 'a', 3.14);                              // OK: outputs 1a3.14
q();                                                   // OK: outputs 1a3.14

end example]

Оператор вызова функции или шаблон оператора объявляется const ( [class.mfct.non-static]) тогда и только тогда, когда за lambda-expression's parameter-declaration-clauseне следует mutable. Он не виртуальный и не декларируемый volatile. Любое noexcept-specifierуказанное в a lambda-expression применяется к соответствующему оператору вызова функции или шаблону оператора. В attribute-specifier-seqa lambda-declaratorпринадлежит типу соответствующего оператора вызова функции или шаблона оператора. Оператор вызова функции или любая заданная специализация шаблона оператора является функцией constexpr, если либо за соответствующими lambda-expression's parameter-declaration-clauseследуют constexpr, либо если она удовлетворяет требованиям для constexpr функции. [ Note: Имена, упомянутые в lambda-declarator, ищутся в контексте, в котором lambda-expressionпоявляется. ] [ end noteExample:

auto ID = [](auto a) { return a; };
static_assert(ID(3) == 3); // OK

struct NonLiteral {
  NonLiteral(int n) : n(n) { }
  int n;
};
static_assert(ID(NonLiteral{3}).n == 3); // ill-formed

end example]

[Example:

auto monoid = [](auto v) { return [=] { return v; }; };
auto add = [](auto m1) constexpr {
  auto ret = m1();
  return [=](auto m2) mutable {
    auto m1val = m1();
    auto plus = [=](auto m2val) mutable constexpr
                   { return m1val += m2val; };
    ret = plus(m2());
    return monoid(ret);
  };
};
constexpr auto zero = monoid(0);
constexpr auto one = monoid(1);
static_assert(add(one)(zero)() == one()); // OK

// Since two below is not declared constexpr, an evaluation of its constexpr member function call operator
// cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture)
// in a constant expression.
auto two = monoid(2);
assert(two() == 2); // OK, not a constant expression.
static_assert(add(one)(one)() == two()); // ill-formed: two() is not a constant expression
static_assert(add(one)(one)() == monoid(2)()); // OK

end example]

Тип закрытия для неуниверсального типа lambda-expressionс no lambda-capture имеет функцию преобразования в указатель на функцию с языком C ++, linkage имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа закрытия. Преобразование происходит в «указатель на noexcept функцию», если оператор вызова функции имеет спецификацию исключения, не вызывающего выброса. Значение, возвращаемое этой функцией преобразования, является адресом функции, F которая при вызове имеет тот же эффект, что и вызов оператора вызова функции закрывающего типа. F является функцией constexpr, если оператор вызова функции является функцией constexpr. Для общей лямбды без no lambda-captureтип закрытия имеет шаблон функции преобразования в указатель на функцию. Шаблон функции преобразования изобретен так же template-parameter-list, а указатель на функцию имеет те же типы параметров, что и шаблон оператора вызова функции. Тип возвращаемого значения указателя на функцию должен вести себя так, как если бы он был decltype-specifierобозначением типа возвращаемого значения соответствующей специализации шаблона оператора вызова функции.

[ Note: Если универсальная лямбда не имеет типа заполнителя trailing-return-typeили trailing-return-typeсодержит тип-заполнитель, необходимо вывести тип возвращаемого значения соответствующей специализации шаблона оператора вызова функции. Соответствующей специализацией является создание экземпляра шаблона оператора вызова функции с теми же аргументами шаблона, что и выведенные для шаблона функции преобразования. Учтите следующее:

auto glambda = [](auto a) { return a; };
int (*fp)(int) = glambda;

Поведение glambda вышеупомянутой функции преобразования похоже на поведение следующей функции преобразования:

struct Closure {
  template<class T> auto operator()(T t) const { ... }
  template<class T> static auto lambda_call_operator_invoker(T a) {
    // forwards execution to operator()(a) and therefore has
    // the same return type deduced
    ...
  }
  template<class T> using fptr_t =
     decltype(lambda_call_operator_invoker(declval<T>())) (*)(T);

  template<class T> operator fptr_t<T>() const
    { return &lambda_call_operator_invoker; }
};

end note]

[Example:

void f1(int (*)(int))   { }
void f2(char (*)(int))  { }

void g(int (*)(int))    { }  // #1
void g(char (*)(char))  { }  // #2

void h(int (*)(int))    { }  // #3
void h(char (*)(int))   { }  // #4

auto glambda = [](auto a) { return a; };
f1(glambda);  // OK
f2(glambda);  // error: ID is not convertible
g(glambda);   // error: ambiguous
h(glambda);   // OK: calls #3 since it is convertible from ID
int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK

end example]

Значение, возвращаемое любой данной специализацией этого шаблона функции преобразования, является адресом функции, F которая при вызове имеет тот же эффект, что и вызов соответствующей специализации шаблона оператора вызова функции универсальной лямбды. F является функцией constexpr, если соответствующая специализация является функцией constexpr. [ Note: Это приведет к неявной реализации тела универсальной лямбды. Тип возвращаемого значения и типы параметров созданного универсального лямбда-выражения должны соответствовать типу возвращаемого значения и типам параметров указателя на функцию. ] [end noteExample:

auto GL = [](auto a) { std::cout << a; return a; };
int (*GL_int)(int) = GL;  // OK: through conversion function template
GL_int(3);                // OK: same as GL(3)

end example]

Функция преобразования или шаблон функции преобразования являются общедоступными, constexpr, не виртуальными, неявными, константными и имеют не-бросание exception specification. [Example:

auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
auto C = [](auto a) { return a; };

static_assert(Fwd(C,3) == 3); // OK

// No specialization of the function call operator template can be constexpr (due to the local static).
auto NC = [](auto a) { static int s; return a; };
static_assert(Fwd(NC,3) == 3); // ill-formed

end example]

В lambda-expression«S compound-statementдает function-body( [dcl.fct.def]) оператор вызова функции, но для целей name lookup, определяющего типа и значение , this и преобразующей со id-expressions ссылкой на нестатические член класса в выражения доступа членов класса с использованием (*this) ( [class.mfct.non-static]), то compound-statementрассматривается в контексте lambda-expression. [Example:

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x + y); // equivalent to S1​::​operator()(this->x + (*this).y)
                                      // this has type S1*
    };
  }
};

end example] Кроме того, переменная _­_­func_­_­ неявно определяется в начале compound-statementиз lambda-expression, с семантикой , как описано в [dcl.fct.def.general].

Тип закрытия, связанный с a, lambda-expressionне имеет конструктора по умолчанию и оператора присваивания удаленной копии. Он имеет конструктор копирования по умолчанию и конструктор перемещения по умолчанию ( [class.copy]). [ Note: Эти специальные функции-члены неявно определены как обычно и поэтому могут быть определены как удаленные. ] end note

Тип закрытия, связанный с a, lambda-expressionимеет неявно объявленный деструктор ( [class.dtor]).

Член закрывающего типа не должен быть явно создан ( [temp.explicit]), явно специализирован ( [temp.expl.spec]) или назван в friend объявлении ( [class.friend]).

8.1.5.2 Captures [expr.prim.lambda.capture]

lambda-capture:
	capture-default
	capture-list
	capture-default , capture-list
capture-default:
	&
	=
capture-list:
	capture ...opt
	capture-list , capture ...opt
capture:
	simple-capture
	init-capture
simple-capture:
	identifier
	& identifier
	this
	* this
init-capture:
	identifier initializer
	& identifier initializer

Тело a lambda-expressionможет ссылаться на переменные с автоматической продолжительностью хранения и *this объект (если есть) охватывающих областей блока путем захвата этих сущностей, как описано ниже.

Если a lambda-captureвключает в себя, capture-defaultто &ни одному идентификатору в a simple-captureиз этого lambda-captureне должно предшествовать &. Если a lambda-captureвключает в себя, capture-defaultто =каждый simple-captureиз них lambda-captureдолжен иметь форму « » или « ». [ Форма является избыточным , но для совместимости с ISO C ++ 2014 ] Игнорирование появления в из , идентификатора или не должен появляться более одного раза в . [& identifier* thisNote: [&,this] end noteinitializers init-capturesthis lambda-captureExample:

struct S2 { void f(int i); };
void S2::f(int i) {
  [&, i]{ };        // OK
  [&, &i]{ };       // error: i preceded by & when & is the default
  [=, *this]{ };    // OK
  [=, this]{ };     // error: this when = is the default
  [i, i]{ };        // error: i repeated
  [this, *this]{ }; // error: this appears twice
}

end example]

A, lambda-expressionчья наименьшая охватывающая область видимости - это block scope a local lambda expression; любой другой lambda-expressionне должен иметь capture-defaultили simple-captureв своем lambda-introducer. reaching scope Локального лямбда - выражения является множество вмещающих прицелы вплоть до самой внутренней функции и ее параметры ограждающих. [ Note: Этот охват включает любое вмешательство lambda-expressions. ] end note

Поиск identifierв a simple-captureвыполняется по обычным правилам для unqualified name lookup; каждый такой поиск должен найти объект. Сущность, обозначенная a simple-capture , называется explicitly capturedи должна быть *this (когда simple-capture это « this» или « * this») или переменной с автоматической продолжительностью хранения, объявленной в области охвата локального лямбда-выражения.

Если a identifierв a simple-captureпоявляется как declarator-idпараметр lambda-declarators parameter-declaration-clause, программа имеет неправильный формат. [Example:

void f() {
  int x = 0;
  auto g = [x](int x) { return 0; }    // error: parameter and simple-capture have the same name
}

end example]

An init-captureведет себя так, как будто он объявляет и явно захватывает переменную формы « », декларативной областью которой является 's , за исключением того, что: auto init-capture ;lambda-expressioncompound-statement

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

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

[ Note: Это позволяет использовать init-captureподобное « x = std​::​move(x)»; второй « x» должен быть привязан к объявлению в окружающем контексте. ] [end noteExample:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ​::​x to 6, and initializes y to 7.

auto z = [a = 42](int a) { return 1; } // error: parameter and local variable have the same name

end example]

A lambda-expressionсо связанным capture-default, который явно не захватывает, *this или переменная с автоматической продолжительностью хранения (это исключает все, id-expression что было обнаружено как относящееся к init-captureсвязанному нестатическому элементу данных), называется implicitly capture сущностью (т. Е. *this Или переменной) если compound-statement:

  • odr-uses сущность (в случае переменной),

  • odr-uses this (в случае объекта, обозначенного *this), или

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

[Example:

void f(int, const int (&)[2] = {})    { }   // #1
void f(const int&, const int (&)[1])  { }   // #2
void test() {
  const int x = 17;
  auto g = [](auto a) {
    f(x);                       // OK: calls #1, does not capture x
  };

  auto g2 = [=](auto a) {
    int selector[sizeof(a) == 1 ? 1 : 2]{};
    f(x, selector);             // OK: is a dependent expression, so captures x
  };
}

end example] Все такие неявно захваченные объекты должны быть объявлены в пределах области действия лямбда-выражения. [ Note: Неявный захват объекта вложенным lambda-expressionможет вызвать его неявный захват содержащим lambda-expression(см. Ниже). Неявное использование odr this может привести к неявному захвату. ] end note

Сущность - captured это если она захвачена явно или неявно. Сущность, захваченная объектом, lambda-expressionнаходится odr-used в области, содержащей lambda-expression. Если *this он захвачен локальным лямбда-выражением, его ближайшая включающая функция должна быть нестатической функцией-членом. Если a lambda-expressionили создание экземпляра шаблона оператора вызова функции универсальной лямбда-выражения odr-uses this или переменной с автоматической продолжительностью хранения из достигаемой области действия, этот объект должен быть захвачен lambda-expression. Если a lambda-expressionзахватывает сущность, и эта сущность не определена или не зафиксирована в непосредственно включающем лямбда-выражении или функции, программа сформирована неправильно. [Example:

void f1(int i) {
  int const N = 20;
  auto m1 = [=]{
    int const M = 30;
    auto m2 = [i]{
      int x[N][M];          // OK: N and M are not odr-used
      x[0][0] = i;          // OK: i is explicitly captured by m2 and implicitly captured by m1
    };
  };
  struct s1 {
    int f;
    void work(int n) {
      int m = n*n;
      int j = 40;
      auto m3 = [this,m] {
        auto m4 = [&,j] {   // error: j not captured by m3
          int x = n;        // error: n implicitly captured by m4 but not captured by m3
          x += m;           // OK: m implicitly captured by m4 and explicitly captured by m3
          x += i;           // error: i is outside of the reaching scope
          x += f;           // OK: this captured implicitly by m4 and explicitly by m3
        };
      };
    }
  };
}

struct s2 {
  double ohseven = .007;
  auto f() {
    return [this] {
      return [*this] {
          return ohseven;   // OK
      }
    }();
  }
  auto g() {
    return [] {
      return [*this] { };   // error: *this not captured by outer lambda-expression
    }();
  }
};

end example]

lambda-expressionПоявляется в аргументе по умолчанию не будет неявно или явно захватить любой объект. [Example:

void f2() {
  int i = 1;
  void g1(int = ([i]{ return i; })());          // ill-formed
  void g2(int = ([i]{ return 0; })());          // ill-formed
  void g3(int = ([=]{ return i; })());          // ill-formed
  void g4(int = ([=]{ return 0; })());          // OK
  void g5(int = ([]{ return sizeof i; })());    // OK
}

end example]

Сущность - это captured by copy если

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

Каждый элемент id-expressionвнутри compound-statementa, lambda-expressionкоторый является odr-use сущностью, захваченной копией, преобразуется в доступ к соответствующему безымянному элементу данных типа закрытия. [ , Что не является ODR использование относится к оригинальной сущности, никогда не члену типа закрытия. Более того, такое не вызывает неявного захвата объекта. ] Если захватывается копией, каждое использование odr преобразуется в указатель на соответствующий безымянный элемент данных типа замыкания, на тип . [ Приведение гарантирует, что преобразованное выражение является prvalue. ] Элемент внутри элемента a, который является случайным использованием ссылки, захваченной посредством ссылки, относится к объекту, к которому привязана захваченная ссылка, а не к захваченной ссылке. [ Действительность таких захватов определяется временем жизни объекта, на который ссылается ссылка, а не временем жизни самой ссылки. ] [Note: id-expressionid-expression end note *this this cast thisNote: end noteid-expressioncompound-statementlambda-expressionNote: end noteExample:

void f(const int*);
void g() {
  const int N = 10;
  [=] {
    int arr[N];     // OK: not an odr-use, refers to automatic variable
    f(&N);          // OK: causes N to be captured; &N points to
                    // the corresponding member of the closure type
  };
}
auto h(int &r) {
  return [&] {
    ++r;            // Valid after h returns if the lifetime of the
                    // object to which r is bound has not ended
  };
}

end example]

Сущность - captured by reference это если она неявно или явно захвачена, но не копируется. Не указано, объявляются ли дополнительные безымянные нестатические элементы данных в типе замыкания для сущностей, захваченных по ссылке. Если объявлено, такие нестатические элементы данных должны иметь буквальный тип. [Example:

// The inner closure type must be a literal type regardless of how reference captures are represented.
static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);

end example] Битовое поле или член анонимного объединения не должны фиксироваться ссылкой.

Если a lambda-expression m2 захватывает сущность, и эта сущность захватывается немедленным включением lambda-expression m1, то m2захват преобразуется следующим образом:

  • если m1 захватывает сущность копией, m2 захватывает соответствующий нестатический член данных типа m1закрытия;

  • если m1 захватывает объект по ссылке, m2 захватывает тот же объект, захваченный m1.

[ Example: Вложенные лямбда-выражения и вызовы, приведенные ниже, будут выведены 123234.

int a = 1, b = 1, c = 1;
auto m1 = [a, &b, &c]() mutable {
  auto m2 = [a, b, &c]() mutable {
    std::cout << a << b << c;
    a = 4; b = 4; c = 4;
  };
  a = 3; b = 3; c = 3;
  m2();
};
a = 2; b = 2; c = 2;
m1();
std::cout << a << b << c;

end example]

Каждое вхождение в скобки , decltype((x)) где x возможно заключенное в скобки id-expressionимя объекта с автоматической продолжительностью хранения, обрабатывается так, как если бы оно x было преобразовано в доступ к соответствующему члену данных типа закрытия, который был бы объявлен, если бы x было использование обозначенной сущности odr. [Example:

void f3() {
  float x, &r = x;
  [=] {                     // x and r are not captured (appearance in a decltype operand is not an odr-use)
    decltype(x) y1;         // y1 has type float
    decltype((x)) y2 = y1;  // y2 has type float const& because this lambda is not mutable and x is an lvalue
    decltype(r) r1 = y1;    // r1 has type float& (transformation not considered)
    decltype((r)) r2 = y2;  // r2 has type float const&
  };
}

end example]

Когда lambda-expressionоценивается, объекты, захваченные копией, используются для прямой инициализации каждого соответствующего нестатического элемента данных результирующего объекта закрытия, а нестатические элементы данных, соответствующие объекту, init-captures инициализируются, как указано соответствующими initializer(которые может быть копией или прямой инициализацией). (Для членов массива элементы массива инициализируются напрямую в порядке возрастания индекса.) Эти инициализации выполняются в (неуказанном) порядке, в котором объявлены нестатические элементы данных. [ Note: Это гарантирует, что разрушения будут происходить в порядке, обратном построению. ]end note

[ Note: Если не ссылочный объект неявно или неявно захватывается ссылкой, вызов оператора вызова функции соответствующего lambda-expression после того, как время жизни объекта закончилось, вероятно, приведет к неопределенному поведению. ]end note

А, simple-captureза которым следует многоточие, - это pack expansion. Знак, init-captureза которым следует многоточие, имеет неправильный формат. [Example:

template<class... Args>
void f(Args... args) {
  auto lm = [&, args...] { return g(args...); };
  lm();
}

end example]

8.1.6 Fold expressions [expr.prim.fold]

Выражение свертки выполняет свертку template parameter pack над бинарным оператором.

fold-expression:
	( cast-expression fold-operator ... )
	( ... fold-operator cast-expression )
	( cast-expression fold-operator ... fold-operator cast-expression )
fold-operator: one of
	+   -   *   /   %   ^   &   |   <<   >> 
	+=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  =
	==  !=  <   >   <=  >=  &&  ||  ,    .*   ->*

Выражение формы, (... op e) где op есть a fold-operator , называется a unary left fold. Выражение формы, (e op ...) где op есть a fold-operator , называется a unary right fold. Унарные левые складки и унарные правые складки называются вместе unary folds. В унарном сгибе cast-expression должен содержать unexpanded parameter pack.

Выражение формы (e1 op1 ... op2 e2) where op1 и op2 are fold-operators называется a binary fold. В двоичной свертке, op1 и op2 должны быть одинаковыми fold-operator, и либо e1 должен содержать нерасширенный пакет параметров, либо e2 должен содержать нерасширенный пакет параметров, но не оба вместе. Если e2 содержит нерасширенный пакет параметров, выражение называется binary left fold. Если e1 содержит нерасширенный пакет параметров, выражение называется binary right fold. [Example:

template<typename ...Args>
bool f(Args ...args) {
  return (true && ... && args); // OK
}

template<typename ...Args>
bool f(Args ...args) {
  return (args + ... + args);   // error: both operands contain unexpanded parameter packs
}

end example]

8.2 Postfix expressions [expr.post]

Постфиксные выражения группируются слева направо.

postfix-expression:
	primary-expression
	postfix-expression [ expr-or-braced-init-list ]
	postfix-expression ( expression-listopt )
	simple-type-specifier ( expression-listopt )
	typename-specifier ( expression-listopt )
	simple-type-specifier braced-init-list
	typename-specifier braced-init-list
	postfix-expression . templateopt id-expression
	postfix-expression -> templateopt id-expression
	postfix-expression . pseudo-destructor-name
	postfix-expression -> pseudo-destructor-name
	postfix-expression ++
	postfix-expression --
	dynamic_cast < type-id > ( expression )
	static_cast < type-id > ( expression )
	reinterpret_cast < type-id > ( expression )
	const_cast < type-id > ( expression )
	typeid ( expression )
	typeid ( type-id )
expression-list:
	initializer-list
pseudo-destructor-name:
	nested-name-specifieropt type-name ::~ type-name
	nested-name-specifier template simple-template-id ::~ type-name
	~ type-name
	~ decltype-specifier

[ Маркер следующего за в , , или может быть продуктом заменяя маркер на два следующих друг за другом маркеров ( ). ]Note: > type-id dynamic_­caststatic_­cast reinterpret_­castconst_­cast >> >[temp.names]end note

8.2.1 Subscripting [expr.sub]

Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксным выражением. Одно из выражений должно быть значением glvalue типа «array of T» или prvalue типа «указатель на T», а другое должно быть prvalue перечисления без области действия или целочисленного типа. Результат имеет тип « T». Тип « T» должен быть полностью определенным типом объекта. 66 Выражение E1[E2] идентично (по определению) *((E1)+(E2)) [ Note: см. [expr.unary] И [expr.add] для деталей * и + и [dcl.array] для деталей массивов. ], за исключением того, что в случае операнда массива результатом является lvalue, если этот операнд является lvalue, и xvalue в противном случае. Выражение упорядочивается перед выражением .end note E1 E2

braced-init-listНе должен использоваться с встроенным индексом оператора.

Это верно , даже если индекс оператор используется в общих идиомы: &x[0].

8.2.2 Function call [expr.call]

Вызов функции - это постфиксное выражение, за которым следуют круглые скобки, содержащие, возможно, пустой, разделенный запятыми список, initializer-clauses который составляет аргументы функции. Постфиксное выражение должно иметь тип функции или тип указателя функции. Для вызова функции, не являющейся членом, или статической функции-члена, постфиксное выражение должно быть либо lvalue, которое относится к функции (в этом случае function-to-pointer standard conversion подавляется в постфиксном выражении), либо оно должно иметь тип указателя функции. Вызов функции через выражение, тип функции которого отличается от типа функции определения вызываемой функции, приводит к неопределенному поведению ( [dcl.link]). Для вызова нестатической функции-члена постфиксное выражение должно быть неявным ( [class.mfct.non-static], [class.static]) или явным class member access , id-expressionкоторое является именем члена функции или pointer-to-member expression выбором члена функции; вызов осуществляется как член объекта класса, на который ссылается выражение объекта. В случае неявного доступа к члену класса подразумеваемый объект - это тот, на который указывает this. [ Note: Вызов функции-члена формы f() интерпретируется как (*this).f() (см. [class.mfct.non-static]). ] Если используется имя функции или функции-члена, имя может быть таким , и в этом случае соответствующая функция должна быть выбрана в соответствии с правилами в . Если выбранная функция не виртуальная или если в выражении доступа к члену класса указано a , эта функция вызывается. В противном случае вызывается его динамический тип выражения объекта; такой вызов называется . [ Динамический тип - это тип объекта, на который ссылается текущее значение выражения объекта. описывает поведение вызовов виртуальных функций, когда выражение объекта относится к объекту, находящемуся в процессе строительства или уничтожения. ]end noteoverloaded [over.match]id-expressionqualified-id final overrider virtual function callNote: [class.cdtor] end note

[ Note: Если используется имя функции или функции-члена, name lookup но не найдено объявление с таким именем, программа имеет неправильный формат. При таком вызове неявно не объявляется никакая функция. ]end note

Если postfix-expressionобозначает destructorтип выражения вызова функции void; в противном случае тип выражения вызова функции - это тип возвращаемого значения статически выбранной функции (т. е. игнорирование virtual ключевого слова), даже если тип фактически вызываемой функции отличается. Этот возвращаемый тип должен быть типом объекта, ссылочным типом или cv void.

Когда функция вызывается, каждый параметр ( [dcl.fct]) должен быть инициализирован ( [dcl.init], [class.copy], [class.ctor]) с соответствующим аргументом. Если функция является нестатической функцией-членом, this parameter объект функции должен быть инициализирован указателем на объект вызова, преобразованным, как если бы с помощью explicit type conversion. [ Note: Для этого преобразования нет проверки доступа или неоднозначности; проверка доступа и устранение неоднозначности выполняются как часть (возможно, неявного) оператора доступа к членам класса. См [class.member.lookup], [class.access.base]и [expr.ref]. ] При вызове функции параметры, имеющие объектный тип, должны иметь полностью определенный объектный тип. [ это по-прежнему позволяет параметру быть указателем или ссылкой на неполный тип класса. Однако это не позволяет параметру, передаваемому по значению, иметь неполный тип класса. ] Это определяется реализацией, заканчивается ли время жизни параметра, когда функция, в которой он определен, возвращается, или в конце включающего полного выражения. Инициализация и уничтожение каждого параметра происходит в контексте вызывающей функции. [ Доступ конструктора, функций преобразования или деструктора проверяется в точке вызова в вызывающей функции. Если конструктор или деструктор для параметра функции вызывает исключение, поиск обработчика начинается в области действия вызывающей функции; в частности, если вызываемая функция имеет обработчик, способный обработать исключение, этот обработчик не рассматривается. ]end noteNote: end noteExample: function-try-blockend example

postfix-expressionСеквенировали перед каждым expressionв expression-list и аргумент любого по умолчанию. Инициализация параметра, включая вычисление каждого связанного значения и побочный эффект, имеет неопределенную последовательность относительно инициализации любого другого параметра. [ Note: Все побочные эффекты оценок аргументов упорядочиваются до ввода функции (см. [intro.execution]). ] [end noteExample:

void f() {
  std::string s = "but I have heard it works even if you don't believe in it";
  s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");
  assert(s == "I have heard it works only if you believe in it"); // OK
}

end example] [ Note: Если операторная функция вызывается с использованием обозначения оператора, вычисление аргументов выполняется в порядке, указанном для встроенного оператора; см [over.match.oper]. ] [end noteExample:

struct S {
  S(int);
};
int operator<<(S, int);
int i, j;
int x = S(i=1) << (i=2);
int y = operator<<(S(j=1), j=2);

После выполнения инициализации значение i равно 2 (см. [expr.shift]), Но не указано, является ли значение j 1 или 2. ]end example

Результатом вызова функции является результат операнда вычисляемой return statement в вызываемой функции (если есть), за исключением вызова виртуальной функции, если тип возвращаемого значения последнего переопределителя отличается от типа возвращаемого значения статически выбранной функции, значение, возвращаемое последним переопределителем, преобразуется в тип возвращаемого значения статически выбранной функции.

[ Функция может изменять значения своих неконстантных параметров, но эти изменения не могут повлиять на значения аргументов, за исключением случаев, когда параметр имеет ссылочный тип ( ); если ссылка относится к типу, уточненному константой , необходимо использовать для отказа от константности, чтобы изменить значение аргумента. Если параметр имеет ссылочного типа временный объект введен в случае необходимости ( , , , , ). Кроме того, можно изменять значения непостоянных объектов с помощью параметров указателя. ]Note: [dcl.ref] const_­cast const [dcl.type] [lex.literal] [lex.string] [dcl.array] [class.temporary]end note

Функцию можно объявить так, чтобы она принимала меньше аргументов (путем объявления default arguments) или большего количества аргументов (с помощью многоточия ..., или параметра функции pack ( [dcl.fct])), чем количество параметров в function definition. [ Note: Это означает, что, за исключением случаев использования многоточия ( ...) или пакета параметров функции, параметр доступен для каждого аргумента. ]end note

Если для данного аргумента нет параметра, аргумент передается таким образом, что принимающая функция может получить значение аргумента путем вызова va_­arg. [ Note: Этот абзац не применяется к аргументам, переданным в пакет параметров функции. Пакеты параметров функции раскрываются во время создания экземпляра шаблона ( [temp.variadic]), поэтому каждый такой аргумент имеет соответствующий параметр при фактическом вызове специализации шаблона функции. ] , И стандартные преобразования выполняются на выражение аргумента. Аргумент, имеющий тип, - это тип . После этих преобразований, если аргумент не имеет арифметических операций, перечисления, указателя, указателя на член или типа класса, программа имеет неправильный формат. Передача потенциально оцененного аргумента наличия нетривиального конструктора копирования, нетривиального конструктора перемещения или нетривиального деструктора без соответствующего параметра условно поддерживается семантикой, определяемой реализацией. Если аргумент имеет целочисленный или перечисляемый тип, который подчиняется , или тип с плавающей запятой, который подчиняется , значение аргумента преобразуется в повышенный тип перед вызовом. Эти рекламные акции называются . end notelvalue-to-rvalue array-to-pointer function-to-pointer cv std​::​nullptr_­t converted void* class type integral promotionsfloating-point promotion default argument promotions

Рекурсивные вызовы разрешены, за исключением main function.

Вызов функции является lvalue, если тип результата является ссылочным типом lvalue или ссылкой rvalue на тип функции, xvalue, если тип результата является ссылкой rvalue на тип объекта, и prvalue в противном случае.

8.2.3 Explicit type conversion (functional notation) [expr.type.conv]

A simple-type-specifier или, typename-specifier за которым следует необязательный аргумент в скобках expression-listили braced-init-list (инициализатор), создает значение указанного типа с учетом инициализатора. Если тип является заполнителем для выведенного типа класса, он заменяется типом возвращаемого значения функции, выбранной разрешением перегрузки class template deduction для оставшейся части этого раздела.

Если инициализатор представляет собой одиночное выражение в скобках, выражение преобразования типа эквивалентно (по определенности и, если определено по смыслу), соответствующему cast expression. Если тип равен, cv void а инициализатор есть (), выражение является значением указанного типа, которое не выполняет инициализацию. В противном случае выражение является prvalue указанного типа, объект результата которого находится direct-initialized с инициализатором. Для выражения формы T(), T не должно быть тип массива.

8.2.4 Pseudo destructor call [expr.pseudo]

Использование оператора pseudo-destructor-nameпосле точки . или стрелки -> представляет деструктор для неклассового типа, обозначенного type-nameили decltype-specifier. Результат должен использоваться только как операнд для оператора вызова функции (), и результат такого вызова имеет тип void. Единственный эффект - это оценка postfix-expressionперед точкой или стрелкой.

Левая часть оператора точки должна быть скалярного типа. Левая часть оператора стрелки должна быть указателем на скалярный тип. Этот скалярный тип является типом объекта. В cv-unqualified версия типа объекта и типа назначенного pseudo-destructor-nameдолжна быть тем же типом. Кроме того, два type-names в pseudo-destructor-nameформе a

nested-name-specifieropt type-name ::~ type-name

должен обозначать один и тот же скалярный тип (игнорируя cv-квалификацию).

8.2.5 Class member access [expr.ref]

Постфиксное выражение, за которым следует точка . или стрелка ->, необязательно, за которым следует ключевое слово template ( [temp.names]), а затем следует за ним id-expression, является постфиксным выражением. Выражение постфикса перед точкой или стрелкой оценивается; 67 результат этой оценки, вместе с id-expression, определяет результат всего постфиксного выражения.

Для первого варианта (точка) первым выражением должно быть значение glvalue, имеющее полный тип класса. Для второго варианта (стрелка) первое выражение должно быть prvalue, имеющим указатель на полный тип класса. Выражение E1->E2 преобразуется в эквивалентную форму (*(E1)).E2; остальная часть [expr.ref] будет адресована только первому варианту (точка). 68 В любом случае он id-expressionдолжен называть члена класса или одного из его базовых классов. [ Note: Поскольку имя класса вставлено в его область действия класса (пункт [class]), имя класса также считается вложенным членом этого класса. ] [ Описывает , как имена ищутся после того , как и операторов. ]end noteNote: [basic.lookup.classref] . -> end note

Сокращение как , называется . Если это битовое поле, то это битовое поле. Тип и ценностная категория определяются следующим образом. В оставшейся части , представляет собой либо или отсутствие , и представляет собой либо или отсутствие . представляет произвольный набор cv-квалификаторов, как определено в .postfix-expression.id-expression E1.E2E1 object expression E2 E1.E2 E1.E2 [expr.ref] cq const const vq volatile volatile cv [basic.type.qualifier]

Если E2 объявлен тип «ссылка на T», то E1.E2 это lvalue; тип E1.E2 есть T. В противном случае применяется одно из следующих правил.

  • Если E2 является членом статических данных и типа E2 IS T, то E1.E2 есть именующий; выражение обозначает названный член класса. Тип E1.E2 есть T.

  • Если E2 это нестатический член данных, тип E1 - « cq1 vq1 X», а тип E2 - « cq2 vq2 T», выражение обозначает именованный член объекта, обозначенного первым выражением. Если E1 - lvalue, то E1.E2 это lvalue; в противном случае E1.E2 - значение x. Пусть это обозначение vq12 означает «объединение» vq1 и vq2; то есть если vq1 или vq2 есть volatile, то vq12 есть volatile. Точно так же пусть обозначение cq12 означает «объединение» cq1 и cq2; то есть если cq1 или cq2 есть const, то cq12 есть const. Если E2 объявлен mutable членом, то тип E1.E2 - « vq12 T». Если E2 не объявлен mutable членом, то тип E1.E2 - « cq12 vq12 T».

  • Если E2 это (возможно, перегруженная) функция-член, функция overload resolution используется для определения того, E1.E2 относится ли она к статической или нестатической функции-члену.

    • Если он относится к статической функции-члену и типом E2 является «функция возврата списка типов-параметров T», то E1.E2 это lvalue; выражение обозначает статическую функцию-член. Тип того E1.E2 же типа, что и E2, а именно «функция возврата списка типов параметров T».

    • В противном случае, если E1.E2 относится к нестатической функции-члену и типом E2 является «функция возврата списка типов-параметров », то это prvalue. Выражение обозначает нестатическую функцию-член. Выражение может использоваться только как левый операнд вызова функции-члена ( ). [ Любой лишний набор круглых скобок, окружающий выражение, игнорируется ( ). ] Тип - «функция возврата списка типов параметров ». cv ref-qualifieropt TE1.E2 [class.mfct]Note: [expr.prim] end note E1.E2 cv T

  • Если E2 это вложенный тип, выражение E1.E2 имеет неправильный формат.

  • Если E2 является перечислителем членов и типом E2 является T, выражение E1.E2 является значением prvalue. Тип E1.E2 есть T.

Если E2 это нестатический член данных или нестатическая функция-член, программа плохо сформирована, если класс, который E2 является непосредственно членом, является неоднозначным base ( [class.member.lookup]) класса именования ( [class.access.base]) из E2. [ Note: Программа также плохо сформирована, если класс именования является неоднозначным основанием типа класса выражения объекта; см [class.access.base]. ]end note

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

Обратите внимание, что (*(E1)) это lvalue.

8.2.6 Increment and decrement [expr.post.incr]

Значение постфиксного ++ выражения - это значение его операнда. [ Note: Полученное значение является копией исходного значения ] Операнд должен быть изменяемым lvalue. Тип операнда должен быть арифметическим типом, отличным от полного типа объекта, или указателем на него. Значение объекта операнда изменяется путем добавления к нему. Значение вычисление выражения секвенировано перед модификацией объекта операнда. Что касается вызова функции с неопределенной последовательностью, операция postfix - это однократная оценка. [ Следовательно, вызов функции не должен вмешиваться между преобразованием lvalue-to-rvalue и побочным эффектом, связанным с любым отдельным оператором postfix ++. ] Результат - prvalue. Тип результата - это cv-неквалифицированная версия типа операнда. Если операнд является битовым полем, которое не может представлять увеличенное значение, результирующее значение битового поля определяется реализацией. См. Также и .end note cv bool 1 ++ ++ Note: end note [expr.add] [expr.ass]

Операнд постфикса -- декрементируется аналогично ++ оператору постфикса . [ Note: Для увеличения и уменьшения префикса см [expr.pre.incr]. ]end note

8.2.7 Dynamic cast [expr.dynamic.cast]

Результат выражения dynamic_­cast<T>(v) - это результат преобразования выражения v в тип T. T должен быть указателем или ссылкой на полный тип класса, или «указателем на cv void». dynamic_­cast Оператор не должен cast away constness.

Если T это тип указателя, v должно быть prvalue указателя на полный тип класса, а результатом будет prvalue типа T. Если T - ссылочный тип lvalue, v должен быть lvalue полного типа класса, а результатом будет lvalue типа, на который ссылается T. Если T это ссылочный тип rvalue, v должно быть glvalue, имеющее полный тип класса, а результатом будет значение x того типа, на который ссылается T.

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

Если значение v является значением нулевого указателя в случае указателя, результатом является значение нулевого указателя типа T.

Если T это «указатель на cv1 B» и v имеет тип «указатель на cv2 D», то B есть базовый класс D, результатом является указатель на уникальный B подобъект D объекта, на который указывает v. Точно так же, если T это «ссылка на cv1 B» и v имеет cv2 D такой тип , который B является базовым классом D, результатом является уникальный B подобъект D объекта, на который ссылается v. 69 Как в случае указателя, так и в случае ссылки, программа плохо сформирована, если cv2 имеет более высокую квалификацию cv, чем недоступный или неоднозначный базовый класс cv1 или если он B является D. [Example:

struct B { };
struct D : B { };
void foo(D* dp) {
  B*  bp = dynamic_cast<B*>(dp);    // equivalent to B* bp = dp;
}

end example]

В противном случае v должен быть указателем или значением glvalue polymorphic type.

Если T это «указатель на cv void», то результатом является указатель на самый производный объект, на который указывает v. В противном случае применяется проверка времени выполнения, чтобы увидеть, может ли объект, на который указывает или на который ссылается, v быть преобразован в тип, на который указывает или на который ссылается T.

Если C это тип класса, на который T указывает или ссылается, проверка времени выполнения логически выполняется следующим образом:

  • Если в наиболее производном объекте, на который указывает (упоминается) v, v указывает (ссылается) на public подобъект базового класса C объекта, и если только один объект типа C является производным от подобъекта, на который указывает (ссылается) v результирующие точки (ссылается ) к этому C объекту.

  • В противном случае, если v указывает (ссылается) на public подобъект базового класса самого производного объекта, а тип самого производного объекта имеет базовый класс типа C, который является однозначным, и publicрезультат указывает (ссылается) на C подобъект самый производный объект.

  • В противном случае проверка времени выполнения fails.

Значение неудачного приведения к типу указателя - это значение нулевого указателя требуемого типа результата. Неудачное приведение к типу ссылки throws an exception на тип, который соответствует handler типу std​::​bad_­cast.

[Example:

class A { virtual void f(); };
class B { virtual void g(); };
class D : public virtual A, private B { };
void g() {
  D   d;
  B*  bp = (B*)&d;                  // cast needed to break protection
  A*  ap = &d;                      // public derivation, no cast needed
  D&  dr = dynamic_cast<D&>(*bp);   // fails
  ap = dynamic_cast<A*>(bp);        // fails
  bp = dynamic_cast<B*>(ap);        // fails
  ap = dynamic_cast<A*>(&d);        // succeeds
  bp = dynamic_cast<B*>(&d);        // ill-formed (not a runtime check)
}

class E : public D, public B { };
class F : public E, public D { };
void h() {
  F   f;
  A*  ap  = &f;                     // succeeds: finds unique A
  D*  dp  = dynamic_cast<D*>(ap);   // fails: yields null; f has two D subobjects
  E*  ep  = (E*)ap;                 // ill-formed: cast from virtual base
  E*  ep1 = dynamic_cast<E*>(ap);   // succeeds
}

end example] [ Note: [class.cdtor] описывает поведение, dynamic_­cast применяемое к строящемуся или разрушающемуся объекту. ]end note

Указанный most derived object или упомянутый by v может содержать другие B объекты в качестве базовых классов, но они игнорируются.

8.2.8 Type identification [expr.typeid]

Результатом typeid выражения является lvalue статического типа и динамического типа, или где - это класс, определяемый реализацией, публично производный от которого сохраняет поведение, описанное в . Время жизни объекта, на который указывает lvalue, продолжается до конца программы. Не определено, вызывается ли деструктор для объекта в конце программы.const std​::​type_­info const std​::​type_­info const name name std​::​type_­info [type.info]70std​::​type_­info

Когда typeid применяется к выражению glvalue, тип которого является polymorphic class типом, результат относится к std​::​type_­info объекту, представляющему тип most derived object (то есть динамический тип), на который ссылается glvalue. Если glvalue выражение получается путем применения унарную * оператора на указатель 71 и указатель является null pointer value, то typeid выражение throws an exception такого типа , который будет соответствовать обработчик типа исключения.std​::​bad_­typeid

Когда typeid он применяется к выражению, отличному от glvalue типа полиморфного класса, результат относится к std​::​type_­info объекту, представляющему статический тип выражения. Lvalue-to-rvalue, array-to-pointerИ function-to-pointer конверсия не применяется к выражению. Если выражение является prvalue, temporary materialization conversion применяется. Выражение - это unevaluated operand.

Когда typeid применяется к a type-id, результат относится к std​::​type_­info объекту, представляющему тип type-id. Если тип type-idявляется ссылкой на возможно cv-квалифицированный тип, результат typeid выражения относится к std​::​type_­info объекту, представляющему cv-неквалифицированный ссылочный тип. Если тип type-idявляется типом класса или ссылкой на тип класса, класс должен быть полностью определен.

Если тип выражения или type-idявляется типом с квалификацией cv, результат typeid выражения ссылается на std​::​type_­info объект, представляющий тип с квалификацией cv. [Example:

class D { /* ... */ };
D d1;
const D d2;

typeid(d1) == typeid(d2);       // yields true
typeid(D)  == typeid(const D);  // yields true
typeid(D)  == typeid(d2);       // yields true
typeid(D)  == typeid(const D&); // yields true

end example]

Если заголовок <typeinfo> не включен до использования typeid, программа имеет неправильный формат.

[ Note: [class.cdtor] описывает поведение, typeid применяемое к объекту, находящемуся в стадии строительства или разрушения. ]end note

Рекомендуемое имя для такого класса extended_­type_­info.

Если p это выражение типа указателя, а затем *p, (*p), *(p), ((*p)), *((p)), и так далее всех удовлетворяют это требование.

8.2.9 Static cast [expr.static.cast]

Результат выражения static_­cast<T>(v) - это результат преобразования выражения v в тип T. Если это ссылочный тип lvalue или ссылка rvalue на тип функции, результатом будет lvalue; если это ссылка rvalue на тип объекта, результатом будет xvalue; в противном случае результатом будет prvalue. Оператор не должен . T T static_­cast cast away constness

- Значение типа « cv1 B», где B это тип класса, может быть приведен к типу «ссылка на cv2 D», где D класс derived от B, если cv2 одно и то же резюме-квалификации , как, или больше резюме , чем-квалификации, cv1. Если B это виртуальный базовый класс D или базовый класс виртуального базового класса D, или если не существует допустимого стандартного преобразования из «указателя на D» в «указатель на B» ( [conv.ptr]), программа имеет неправильный формат. Значение xvalue типа « cv1 B» может быть приведено к типу «ссылка rvalue на cv2 D» с теми же ограничениями, что и для lvalue типа « cv1 B». Если объект типа « cv1 B» на самом деле является подобъектом базового класса объекта типа D, результат относится к включающему объекту типа D. В противном случае поведение не определено. [Example:

struct B { };
struct D : public B { };
D d;
B &br = d;

static_cast<D&>(br);            // produces lvalue to the original d object

end example]

Lvalue типа « cv1 T1» может быть приведено к типу «ссылка rvalue на cv2 T2», если « cv2 T2» находится reference-compatible с « cv1 T1». Если значение не является битовым полем, результат относится к объекту или его подобъекту указанного базового класса; в противном случае, lvalue-to-rvalue conversion применяется к битовому полю, а результирующее значение prvalue используется как expressionзначение static_­cast для оставшейся части этого раздела. Если T2 - inaccessible или ambiguous базовый класс T1, программа, которая требует такого приведения, плохо сформирована.

Выражение e может быть явно преобразовано в тип, T если существует implicit conversion sequence от e до Tили если разрешение перегрузки для direct-initialization объекта или ссылки типа T from e найдет хотя бы одну жизнеспособную функцию ( [over.match.viable]). Если T это ссылочный тип, эффект такой же, как при объявлении и инициализации.

 T t(e);

для какой-то придуманной временной переменной t ( [dcl.init]), а затем использовать временную переменную как результат преобразования. В противном случае объект результата инициализируется напрямую из e. [ Note: Преобразование неправильно сформировано при попытке преобразовать выражение типа класса в недоступный или неоднозначный базовый класс. ]end note

В противном случае static_­cast будет выполнено одно из преобразований, перечисленных ниже. Никакое другое преобразование не должно выполняться явно с использованием файла static_­cast.

Любое выражение может быть явно преобразовано в тип cv void, и в этом случае оно становится discarded-value expression. [ Note: Однако, если значение находится в a temporary object, деструктор для этого объекта не выполняется до обычного времени, а значение объекта сохраняется для выполнения деструктора. ]end note

Обратный любой , standard conversion sequence не содержащий lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, boolean, или function pointer преобразования, может быть выполнены с использованием явно static_­cast. Программа плохо сформирована, если она используется static_­cast для выполнения обратной неверной стандартной последовательности преобразования. [Example:

struct B { };
struct D : private B { };
void f() {
  static_cast<D*>((B*)0);               // error: B is a private base of D
  static_cast<int B::*>((int D::*)0);   // error: B is a private base of D
}

end example]

lvalue-to-rvalue, array-to-pointerИ function-to-pointer преобразования применяются в операнд. На такой static_­cast объект действует ограничение, которое не предусмотрено явным преобразованием cast away constness, а также следующие дополнительные правила для конкретных случаев:

Значение a scoped enumeration type можно явно преобразовать в целочисленный тип. Когда этот тип равен cv bool, результирующее значение будет, false если исходное значение равно нулю и true для всех остальных значений. Для остальных целочисленных типов значение не изменяется, если исходное значение может быть представлено указанным типом. В противном случае результирующее значение не указано. Значение типа перечисления с заданной областью также может быть явно преобразовано в тип с плавающей запятой; результат такой же, как и при преобразовании исходного значения в тип с плавающей запятой.

Значение целочисленного или перечислимого типа может быть явно преобразовано в полный перечисляемый тип. Значение не изменяется, если исходное значение находится в диапазоне значений перечисления ( [dcl.enum]). В противном случае поведение не определено. Значение типа с плавающей запятой также можно явно преобразовать в тип перечисления. Результирующее значение совпадает с преобразованием исходного значения в базовый тип enumeration ( [conv.fpint]), а затем в тип перечисления.

Prvalue типа «указатель на cv1 B», где B является типом класса, может быть преобразовано в prvalue типа «указатель на cv2 D», где D - это класс derived из B, если cv2 это такая же квалификация cv или более высокая квалификация, чем, cv1. Если B это виртуальный базовый класс D или базовый класс виртуального базового класса D, или если не существует допустимого стандартного преобразования из «указателя на D» в «указатель на B» ( [conv.ptr]), программа имеет неправильный формат. null pointer value Преобразуется в значение указателя нулевого типа назначения. Если prvalue типа «указатель на cv1 B» указывает на, B который на самом деле является подобъектом объекта типа D, результирующий указатель указывает на включающий объект типа D. В противном случае поведение не определено.

Prvalue типа «указатель на член D типа cv1 T» может быть преобразован в prvalue типа «указатель на член B типа cv2 T», где B равно a base class of D, если cv2 это такая же квалификация cv или более высокая квалификация cv, чем, cv1. 72 Если не существует допустимого стандартного преобразования из «указателя на член B типа T» в «указатель на член D типа T» ( [conv.mem]), программа сформирована неправильно. null member pointer value Преобразуются в значение указателя члена нуля типа назначения. Если класс B содержит исходный член или является базовым или производным классом класса, содержащего исходный член, результирующий указатель на член указывает на исходный член. В противном случае поведение не определено. [ Note: Хотя класс B не обязательно должен содержать исходный член, динамический тип объекта, с которым выполняется косвенное обращение через указатель на член, должен содержать исходный член; см [expr.mptr.oper]. ]end note

Prvalue типа «указатель cv1 void» может быть преобразовано в prvalue типа «указатель на cv2 T», где T это тип объекта , и cv2 это тот же CV-квалификацию или более CV-квалификацию , чем, cv1. Если исходное значение указателя представляет собой адрес A байта в памяти и A не удовлетворяет требованию выравнивания T, то результирующее значение указателя не указано. В противном случае, если значение исходных точки указателя на объект a, и есть объект b типа T (без учета CV-квалификация) , который pointer-interconvertible с a, результатом является указателем b. В противном случае значение указателя не изменится при преобразовании. [Example:

T* p1 = new T;
const T* p2 = static_cast<const T*>(static_cast<void*>(p1));
bool b = p1 == p2;  // b will have the value true.

end example]

Типы функций (включая те, которые используются в указателях на типы функций-членов) никогда не квалифицируются cv; см [dcl.fct].

8.2.10 Reinterpret cast [expr.reinterpret.cast]

Результат выражения reinterpret_­cast<T>(v) - это результат преобразования выражения v в тип T. Если это ссылочный тип lvalue или ссылка rvalue на тип функции, результатом будет lvalue; если это ссылка rvalue на тип объекта, результатом будет xvalue; в противном случае результатом является prvalue , и для выражения выполняются стандартные преобразования . Преобразования, которые можно выполнить явно с помощью , перечислены ниже. Никакое другое преобразование не может быть выполнено явно с помощью . T T lvalue-to-rvalue array-to-pointer function-to-pointer v reinterpret_­cast reinterpret_­cast

reinterpret_­cast Оператор не должен cast away constness. Выражение целочисленного типа, типа перечисления, указателя или типа указатель на член может быть явно преобразовано в его собственный тип; такое приведение дает значение своего операнда.

[ Note: Отображение, выполненное с помощью, reinterpret_­cast может, а может и не дать представление, отличное от исходного значения. ]end note

Указатель может быть явно преобразован в любой целочисленный тип, достаточно большой, чтобы вместить его. Функция сопоставления определяется реализацией. [ Note: Предполагается, что это неудивительно для тех, кто знает структуру адресации базовой машины. ] Значение типа может быть преобразовано в целочисленный тип; преобразование имеет то же значение и действительность, что и преобразование в целочисленный тип. [ A нельзя использовать для преобразования значения любого типа в тип . ]end note std​::​nullptr_­t (void*)0 Note: reinterpret_­caststd​::​nullptr_­t end note

Значение целочисленного типа или типа перечисления может быть явно преобразовано в указатель. Указатель, преобразованный в целое число достаточного размера (если такое существует в реализации) и обратно в тот же тип указателя, будет иметь свое исходное значение; сопоставления между указателями и целыми числами в противном случае определяются реализацией. [ Note: За исключением случаев, описанных в [basic.stc.dynamic.safety], результатом такого преобразования не будет безопасно полученное значение указателя. ] end note

Указатель на функцию можно явно преобразовать в указатель на функцию другого типа. [ Note: Эффект от вызова функции через указатель на тип функции ( [dcl.fct]), который не совпадает с типом, используемым в определении функции, не определен. ] За исключением того, что преобразование prvalue типа «указатель на » в тип «указатель на » (где и являются типами функций) и обратно к исходному типу дает исходное значение указателя, результат такого преобразования указателя не указан. [ См. Также более подробную информацию о преобразованиях указателей. ]end note T1T2 T1 T2 Note: [conv.ptr] end note

Указатель объекта может быть явно преобразован в указатель объекта другого типа. 73 Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта «указатель на cv T», результатом является static_­cast<cv T*>(static_­cast<cv void*>(v)). [ Note: Преобразование prvalue типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не строже, чем у T1) и обратно в исходный тип дает исходное значение указателя. ]end note

Преобразование указателя функции в тип указателя объекта или наоборот поддерживается условно. Значение такого преобразования определяется реализацией, за исключением того, что если реализация поддерживает преобразования в обоих направлениях, преобразование prvalue одного типа в другой тип и обратно, возможно, с другой квалификацией cv, должно дать исходное значение указателя.

null pointer value Преобразуется в значение указателя нулевого типа назначения. [ Note: Константа нулевого указателя типа std​::​nullptr_­t не может быть преобразована в тип указателя, а константа нулевого указателя целочисленного типа не обязательно преобразуется в значение нулевого указателя. ]end note

Prvalue типа «указатель на член X типа T1» может быть явно преобразован в prvalue другого типа «указатель на член Y типа T2», если T1 и T2 являются типами функций, или обоими типами объектов. Преобразуются в значение указателя члена нуля типа назначения. Результат этого преобразования не указан, за исключением следующих случаев:74 null member pointer value

  • преобразование prvalue типа «указатель на функцию-член» в другой указатель на тип функции-члена и обратно в исходный тип дает исходный указатель на значение элемента.

  • преобразование prvalue типа «указатель на элемент данных X типа T1» в тип «указатель на элемент данных Y типа T2» (где требования к выравниванию T2 не строже, чем у T1) и обратно к исходному типу дает исходный указатель на значение члена.

Выражение типа glvalue типа T1 может быть приведено к типу «ссылка на T2», если выражение типа «указатель на T1» может быть явно преобразовано в тип «указатель на T2» с помощью reinterpret_­cast. Результат относится к тому же объекту, что и исходное значение glvalue, но с указанным типом. [ Note: То есть, для lvalues, ссылка литой reinterpret_­cast<T&>(x) имеет тот же эффект, что и преобразование *reinterpret_­cast<T*>(&x) с помощью встроенного & и * операторы (и аналогично для reinterpret_­cast<T&&>(x)). ] Нет временно создаются, копия не сделана, и или не называется. end noteconstructors conversion functions 75

Типы могут иметь разные cv-qualifiers, с учетом общего ограничения, что a reinterpret_­cast не может отбросить константность.

T1 и T2 может иметь разные cv-qualifiers, с учетом общего ограничения, что a reinterpret_­cast не может отбросить константность.

Иногда его называют файлом type pun.

8.2.11 Const cast [expr.const.cast]

Результат выражения const_­cast<T>(v) имеет тип T. Если T это lvalue ссылка на тип объекта, результатом будет lvalue; если T это ссылка rvalue на тип объекта, результатом будет xvalue; в противном случае результатом является prvalue lvalue-to-rvalue, array-to-pointerи function-to-pointer для выражения выполняются стандартные преобразования v. Преобразования, которые можно выполнить явно с помощью const_­cast , перечислены ниже. Никакое другое преобразование не должно выполняться явно с использованием const_­cast.

Note: соответствии с ограничениями в этом разделе выражение может быть приведено к собственному типу с помощью const_­cast оператора. ]end note

Для двух similar types T1 и T2prvalue типа T1 может быть явно преобразовано в тип T2 с помощью const_­cast. Результат const_­cast ссылается на исходную сущность. [Example:

typedef int *A[3];               // array of 3 pointer to int
typedef const int *const CA[3];  // array of 3 const pointer to const int

CA &&r = A{}; // OK, reference binds to temporary array object after qualification conversion to type CA
A &&r1 = const_cast<A>(CA{});    // error: temporary array decayed to pointer
A &&r2 = const_cast<A&&>(CA{});  // OK

end example]

Для двух типов объектов T1 и T2, если указатель на T1 может быть явно преобразован в тип «указатель на T2» с помощью a const_­cast, то также могут быть выполнены следующие преобразования:

  • lvalue типа T1 может быть явно преобразовано в lvalue типа T2 с помощью приведения const_­cast<T2&>;

  • glvalue типа T1 может быть явно преобразован в xvalue типа T2 с помощью приведения const_­cast<T2&&>; а также

  • если T1 это тип класса, prvalue типа T1 можно явно преобразовать в xvalue типа T2 с помощью приведения const_­cast<T2&&>.

Результат ссылки const_­cast относится к исходному объекту, если операнд является значением glvalue, и к результату применения в temporary materialization conversion противном случае.

A null pointer value преобразуется в значение нулевого указателя целевого типа. null member pointer value Преобразуются в значение указателя члена нуля типа назначения.

[ Note: В зависимости от типа объекта операция записи через указатель, lvalue или указатель на член данных в результате, const_­cast который отбрасывает константный квалификатор, 76 может привести к неопределенному поведению ( [dcl.type.cv]). ]end note

Преобразование из типа T1 к типу , T2 casts away constness если T1 и T2 различны, есть cv-decomposition из T1 получая n таким образом, что T2 имеет CV-разложение вида cv20 P20 cv21 P21 cv2n1 P2n1 cv2n U2, и нет квалификации преобразования , который преобразует T1 с cv20 P10 cv21 P11 cv2n1 P1n1 cv2n U1.

Преобразование lvalue типа T1 в lvalue типа T2 с использованием ссылочного преобразования lvalue или преобразование из выражения типа T1 в xvalue типа T2 с использованием ссылочного преобразования rvalue отбрасывает константность, если преобразование из prvalue типа «указатель на T1» на тип «указатель на T2» отбрасывает константность.

[ Note: Некоторые преобразования, которые включают только изменения в cv-квалификации, не могут быть выполнены с использованием. const_­cast. Например, преобразования между указателями на функции не рассматриваются, потому что такие преобразования приводят к значениям, использование которых вызывает неопределенное поведение. По тем же причинам преобразования между указателями на функции-члены и, в частности, преобразование указателя на константную функцию-член в указатель на неконстантную функцию-член не рассматриваются. ]end note

const_­cast не ограничивается преобразованиями, которые отбрасывают квалификатор const.

8.3 Unary expressions [expr.unary]

Выражения с унарными операторами группируются справа налево.

unary-expression:
	postfix-expression
	++ cast-expression
	-- cast-expression
	unary-operator cast-expression
	sizeof unary-expression
	sizeof ( type-id )
	sizeof ... ( identifier )
	alignof ( type-id )
	noexcept-expression
	new-expression
	delete-expression

unary-operator: one of
	*  &  +  -  !  ~

8.3.1 Unary operators [expr.unary.op]

Унарный * оператор выполняет indirection: выражение, к которому он применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является lvalue, относящееся к объекту или функции, на которые указывает выражение. Если тип выражения - «указатель на T», тип результата - « T». [ Косвенное указание на неполный тип (кроме ) допустимо. Полученное таким образом lvalue можно использовать ограниченными способами (например, для инициализации ссылки); это lvalue нельзя преобразовывать в prvalue, см . ]Note: cv void [conv.lval]end note

Результатом каждого из следующих унарных операторов является prvalue.

Результатом унарного & оператора является указатель на его операнд. Операнд должен быть lvalue или a qualified-id. Если операнд представляет собой qualified-idименование нестатического или вариантного члена m некоторого класса C с типом T, результат имеет тип «указатель на член класса C типа T» и является обозначением prvalue C​::​m. В противном случае, если тип выражения - это T, результат имеет тип «указатель на T» и является значением prvalue, которое является адресом назначенного объекта ( [intro.memory]) или указателем на назначенную функцию. [ Note: В частности, адрес объекта типа « cv T» - «указатель на cv T» с той же квалификацией cv. ] В целях арифметики указателя ( ) и сравнения ( , ) объект, не являющийся элементом массива, адрес которого берется таким образом, считается принадлежащим к массиву с одним элементом типа . [ end note[expr.add][expr.rel] [expr.eq] TExample:

struct A { int i; };
struct B : A { };
... &B::i ...       // has type int A​::​*
int a;
int* p1 = &a;
int* p2 = p1 + 1;   // defined behavior
bool b = p2 > p1;   // defined behavior, with value true

end example] [ Note: Указатель на член, сформированный из mutable нестатического члена данных ( [dcl.stc]), не отражает mutable спецификатор, связанный с нестатическим членом данных. ]end note

Указатель на член формируется только тогда, когда используется явное значение, & а его операнд qualified-idне заключен в круглые скобки. [ Note: То есть выражение &(qualified-id), в котором qualified-idзаключено в круглые скобки, не образует выражение типа «указатель на член». Также нет qualified-id, потому что не существует неявного преобразования из a qualified-idдля нестатической функции-члена в тип «указатель на функцию-член», как из lvalue типа функции в тип «указатель на функцию» ( [conv.func]). Также нет &unqualified-id указателя на член, даже в рамках unqualified-idкласса. ]end note

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

Адрес overloaded function может быть взят только в контексте, который однозначно определяет, к какой версии перегруженной функции относится ссылка (см [over.over]. Раздел "Ресурсы" ). [ Note: Поскольку контекст может определять, является ли операнд статической или нестатической функцией-членом, контекст также может влиять на то, имеет ли выражение тип «указатель на функцию» или «указатель на функцию-член». ]end note

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

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

Операнд логической операции отрицания ! является contextually converted to bool; его значение равно, true если преобразованный операнд есть, false и в false противном случае. Тип результата bool.

Операнд ~ должен иметь целочисленный или незадействованный перечислимый тип; результатом является дополнение до единиц своего операнда. Выполняются комплексные акции. Тип результата - это тип продвинутого операнда. В грамматике возникает двусмысленность, когда ~ следует class-nameили decltype-specifier. Неоднозначность разрешается путем рассмотрения ~ как унарного оператора дополнения, а не как начало unqualified-id именования деструктора. [ Note: Поскольку грамматика не разрешает оператору следовать за ., ->или ​::​ токенами, ~ за которым следует class-nameили decltype-specifierв выражении доступа к члену, либо qualified-idоднозначно анализируется как имя деструктора. ]end note

8.3.2 Increment and decrement [expr.pre.incr]

Операнд префикса изменяется добавлением . Операнд должен быть изменяемым значением. Тип операнда должен быть арифметическим типом, отличным от полностью определенного типа объекта, или указателем на него. Результат - обновленный операнд; это lvalue, и это битовое поле, если операнд является битовым полем. Выражение эквивалентно . [ См. Обсуждения и информацию о преобразованиях. ] ++ 1 cv bool ++x x+=1Note: addition assignment operators end note

Операнд префикса -- изменяется вычитанием 1. Требования к операнду префикса -- и свойствам его результата в остальном такие же, как и у префикса ++. [ Note: Для постфиксного увеличения и уменьшения см [expr.post.incr].. ]end note

8.3.3 Sizeof [expr.sizeof]

sizeof Оператор дает число байтов в объекте представления операнда. Операнд может быть выражением unevaluated operandили заключенным в скобки type-id. Оператор не должен быть применен к выражению , которое имеет функцию или неполный тип, в скобках название таких типов, или к glvalue , что обозначает битовое поле. , и есть . Результат применения к любому другому определяется реализацией. [ В частности, , , , и являются реализация. ] [ См. Определение и определение . ] sizeof sizeof(char) sizeof(signed char) sizeof(unsigned char) 1sizeof fundamental type Note: sizeof(bool) sizeof(char16_­t)sizeof(char32_­t) sizeof(wchar_­t) 77 end noteNote: [intro.memory] byte [basic.types] object representationend note

При применении к ссылке или ссылочному типу результатом является размер ссылочного типа. При применении к классу результатом является количество байтов в объекте этого класса, включая любые отступы, необходимые для размещения объектов этого типа в массиве. Размер a most derived class должен быть больше нуля. Результатом применения sizeof к подобъекту базового класса является размер типа базового класса. При применении к массиву результатом является общее количество байтов в массиве. Это означает, что размер массива элементов в несколько раз превышает размер элемента.78 n n

sizeof Оператор может быть применен к указателю на функцию, но не должен применяться непосредственно к функции.

В lvalue-to-rvalue, array-to-pointerи function-to-pointer стандартные преобразования не применяется к операнду sizeof. Если операнд является prvalue, temporary materialization conversion применяется.

Идентификатор в sizeof... выражении должен называть пакет параметров. sizeof... Оператор дает количество аргументов , предусмотренным для параметра пакета identifier. sizeof... Выражение является pack expansion. [Example:

template<class... Types>
struct count {
  static const std::size_t value = sizeof...(Types);
};

end example]

Результат sizeof и sizeof... является константой типа std​::​size_­t. [ определяется в стандартном заголовке ( , ). ]Note: std​::​size_­t <cstddef> [cstddef.syn] [support.types.layout]end note

sizeof(bool) не требуется 1.

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

8.3.4 New [expr.new]

Эти new-expressionпопытки создать объект из type-idили new-type-idк которому она применяется. Тип этого объекта - allocated type. Этот тип должен быть полный тип объекта, но не абстрактный тип класса или их массив ( [intro.object], [basic.types], [class.abstract]). [ Note: Поскольку ссылки не являются объектами, ссылки не могут быть созданы с помощью new-expressions. ] [ Может быть резюме квалифицированных типа, в этом случае объект , созданный имеет резюме квалифицированных типа. ]end noteNote: type-idnew-expressionend note

new-expression:
	::opt new new-placementopt new-type-id new-initializeropt 
	::opt new new-placementopt ( type-id ) new-initializeropt

new-placement:
	( expression-list )
new-type-id:
	type-specifier-seq new-declaratoropt
new-declarator:
	ptr-operator new-declaratoropt 
	noptr-new-declarator
noptr-new-declarator:
	[ expression ] attribute-specifier-seqopt
	noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
new-initializer:
	( expression-listopt )
	braced-init-list

Объекты , созданные new-expressionимеют dynamic storage duration. [ Время существования такой сущности не обязательно ограничивается областью, в которой она создана. ] Если сущность не является объектом массива, возвращает указатель на созданный объект. Если это массив, возвращает указатель на начальный элемент массива.Note: end notenew-expressionnew-expression

Если a placeholder type появляется в type-specifier-seqa new-type-idили type-ida new-expression, выделенный тип выводится следующим образом: Пусть init будет the new-initializer, если есть, и T будет the new-type-idor type-idof the new-expression, тогда выделенный тип - это тип, выведенный для переменной x в изобретенном объявлении ( [dcl.spec.auto]) :

T x init ;

[Example:

new auto(1);                    // allocated type is int
auto x = new auto('a');         // allocated type is char, x is of type char*

template<class T> struct A { A(T, T); };
auto y = new A{1, 2};           // allocated type is A<int>

end example]

В new-type-ida new-expression- самая длинная возможная последовательность new-declarators. [ Note: Это предотвращает неоднозначность между описателем операторами &, &&, *и [] и их экспрессиями аналогами. ] [end noteExample:

new int * i;                    // syntax error: parsed as (new int*) i, not as (new int)*i

Это * декларатор указателя, а не оператор умножения. ]end example

[ Скобки в из может иметь неожиданные последствия. [Note: new-type-idnew-expressionExample:

new int(*[10])();               // error

неправильно сформирован, потому что привязка

(new int) (*[10])();            // error

Вместо этого явно заключенная в скобки версия new оператора может использоваться для создания объектов compound types:

new (int (*[10])());

выделяет массив 10 указателей на функции (без аргументов и с возвратом int). ] ]end exampleend note

Когда выделенный объект является массивом (то есть используется noptr-new-declaratorсинтаксис или new-type-idили type-idобозначает тип массива), new-expressionвыдает указатель на начальный элемент (если есть) массива. [ Note: Оба new int и new int[10] имеют тип, int* а тип new int[i][10] - это ] В a принадлежит к соответствующему типу массива. int (*)[10] end noteattribute-specifier-seqnoptr-new-declarator

Каждый constant-expressionэлемент a noptr-new-declaratorдолжен быть converted constant expression типа a std​::​size_­t и иметь строго положительное значение. Объект expressionв a noptr-new-declaratorнеявно преобразуется в std​::​size_­t. [ Example: Принимая во внимание определение int n = 42, new float[n][5] правильно сформирован (потому что n это expressionв А noptr-new-declarator), но new float[5][n] плохо сформированные (потому что n это не выражение постоянной). ]end example

В expressiona noptr-new-declaratorошибочно, если:

  • выражение неклассового типа и его значение перед преобразованием в std​::​size_­t меньше нуля;

  • выражение относится к классу и его значение до применения второго стандартного преобразования ( [over.ics.user]) 79 меньше нуля;

  • его значение таково, что размер выделенного объекта будет превышать размер, определенный реализацией limit; или

  • new-initializerявляется braced-init-listи количество элементов массива , для которых предусмотрены инициализаторов (включая завершающий '\0' в string literal) превышает количество элементов , чтобы инициализировать.

Если expressionпосле преобразования в std​::​size_­t:

  • если expressionявляется основным постоянным выражением, программа имеет неправильный формат;

  • в противном случае функция распределения не вызывается; вместо

    • если функция выделения, которая должна была быть вызвана, имеет спецификацию исключения, не вызывающего выброса ( [except.spec]), значением new-expression является значение нулевого указателя требуемого типа результата;

    • в противном случае new-expressionзавершается выдача исключения типа, который соответствует типу handler ( [except.handle]) std​::​bad_­array_­new_­length.

Когда значение expressionравно нулю, вызывается функция выделения для выделения массива без элементов.

A new-expressionможет получить хранилище для объекта, вызвав функцию распределения ( [basic.stc.dynamic.allocation]). Если new-expressionзавершается выдачей исключения, он может освободить хранилище, вызвав a deallocation function. Если выделенный тип не является массивом, имя функции выделения - это, а имя функции освобождения - . Если выделенный тип является типом массива, имя функции распределения - это, а имя функции освобождения - . [ Реализация должна дать определения по умолчанию для глобальных функций распределения ( , , ). Программа на C ++ может предоставлять альтернативные определения этих функций ( ) и / или версий для конкретных классов ( ). Набор функций выделения и освобождения, который может быть вызван a, может включать в себя функции, которые не выполняют выделение или освобождение; например, см . ]operator new operator deleteoperator new[]operator delete[]Note: [basic.stc.dynamic] [new.delete.single] [new.delete.array][replacement.functions][class.free]new-expression [new.delete.placement]end note

Если new-expressionначинается с унарного ​::​ оператора, имя функции распределения ищется в глобальной области. В противном случае, если выделенный тип является типом класса T или его массивом, имя функции распределения ищется в области видимости T. Если этот поиск не может найти имя или если выделенный тип не является типом класса, имя функции распределения ищется в глобальной области.

Реализации разрешено опускать вызов заменяемой функции глобального распределения ( [new.delete.single], [new.delete.array]). Когда это происходит, хранилище вместо этого предоставляется реализацией или предоставляется путем расширения выделения другой new-expression. Реализация может расширить выделение a, new-expression e1 чтобы предоставить хранилище для a, new-expression e2 если бы было верно следующее, если бы выделение не было расширено:

  • оценка e1 выполняется до оценки e2, и

  • e2 оценивается всякий раз, когда e1 получает память, и

  • оба e1 и e2 вызывают одну и ту же заменяемую функцию глобального распределения, и

  • если функция распределения, вызванная e1 и e2 выбрасывает, любые исключения, возникшие при оценке любого из e1 или e2 , будут сначала перехвачены в том же обработчике, и

  • значения указателя, созданные e1 и e2 являются операндами для оценки delete-expressions, и

  • оценка e2 выполняется перед оценкой того, delete-expressionчей операнд является значением указателя, созданным e1.

[Example:

  void mergeable(int x) {
    // These allocations are safe for merging:
    std::unique_ptr<char[]> a{new (std::nothrow) char[8]};
    std::unique_ptr<char[]> b{new (std::nothrow) char[8]};
    std::unique_ptr<char[]> c{new (std::nothrow) char[x]};

    g(a.get(), b.get(), c.get());
  }

  void unmergeable(int x) {
    std::unique_ptr<char[]> a{new char[8]};
    try {
      // Merging this allocation would change its catch handler.
      std::unique_ptr<char[]> b{new char[x]};
    } catch (const std::bad_alloc& e) {
      std::cerr << "Allocation failed: " << e.what() << std::endl;
      throw;
    }
  }

end example]

Когда a new-expressionвызывает функцию выделения и это выделение не было расширено, он new-expressionпередает запрошенный объем пространства в функцию выделения в качестве первого аргумента типа std​::​size_­t. Этот аргумент должен быть не меньше размера создаваемого объекта; он может быть больше размера создаваемого объекта, только если объект является массивом. Для массивов char, unsigned charи std​::​byte, разность между результатом new-expressionи адресом , возвращаемым функцией распределения должна быть целым кратной строжайшим фундаментальным alignment requirement любым типа объекта, размер которого не больше , чем размер массива создается. [ Поскольку предполагается, что функции распределения возвращают указатели на хранилище, которое соответствующим образом выровнено для объектов любого типа с фундаментальным выравниванием, это ограничение накладных расходов на выделение массива допускает общую идиому выделения массивов символов, в которые позже будут помещены объекты других типов. ]Note: end note

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

new-placementСинтаксис используется для предоставления дополнительных аргументов в функцию распределения; такое выражение называется a placement new-expression.

Разрешение перегрузки выполняется при вызове функции, созданной путем сборки списка аргументов. Первый аргумент - это объем запрошенного пространства и имеет тип std​::​size_­t. Если тип выделенного объекта имеет выравнивание по новому-расширенному, следующий аргумент - это выравнивание типа и имеет тип std​::​align_­val_­t. Если используется new-placementсинтаксис, initializer-clauses в нем expression-list находятся следующие аргументы. Если совпадающая функция не найдена и у выделенного типа объекта есть выравнивание "новое-расширенное", аргумент выравнивания удаляется из списка аргументов, и снова выполняется разрешение перегрузки.

[Example:

  • new T приводит к одному из следующих вызовов:

    operator new(sizeof(T))
    operator new(sizeof(T), std::align_val_t(alignof(T)))
  • new(2,f) T приводит к одному из следующих вызовов:

    operator new(sizeof(T), 2, f)
    operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
  • new T[5] приводит к одному из следующих вызовов:

    operator new[](sizeof(T) * 5 + x)
    operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)))
  • new(2,f) T[5] приводит к одному из следующих вызовов:

    operator new[](sizeof(T) * 5 + x, 2, f)
    operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)), 2, f)

Здесь каждый экземпляр x - неотрицательное неопределенное значение, представляющее накладные расходы на выделение массива; результат new-expressionбудет компенсирован на эту сумму от значения, возвращаемого operator new[]. Эти накладные расходы могут применяться ко всем массивам new-expressions, включая те, которые относятся к библиотечной функции operator new[](std​::​size_­t, void*) и другим функциям размещения. Сумма накладных расходов может варьироваться от одного вызова new к другому. ]end example

[ Note: Если функция распределения не имеет не-выброса exception specification, это указывает на сбой выделения памяти путем выдачи исключения ( , пункт , ); в противном случае он возвращает ненулевой указатель. Если функция выделения имеет спецификацию исключения, не вызывающего выброса, она возвращает значение null, чтобы указать на сбой выделения памяти, и ненулевой указатель в противном случае. ] Если функция распределения - это form ( ) без выделения памяти, которая возвращает значение null, поведение не определено. В противном случае, если функция распределения возвращает ноль, инициализация не должна выполняться, функция освобождения не должна вызываться, а значение должно быть нулевым.std​::​bad_­alloc [basic.stc.dynamic.allocation] [except] [bad.alloc]end note[new.delete.placement]new-expression

[ Note: Когда функция распределения возвращает значение, отличное от нуля, это должен быть указатель на блок памяти, в котором было зарезервировано пространство для объекта. Предполагается, что блок памяти правильно выровнен и имеет запрошенный размер. Адрес созданного объекта не обязательно будет таким же, как у блока, если объект является массивом. ]end note

A, new-expressionкоторый создает объект типа, T инициализирует этот объект следующим образом:

  • Если new-initializerопущен, объект инициализируется по умолчанию ( [dcl.init]). [ Note: Если инициализация не выполняется, объект имеет неопределенное значение. ] end note

  • В противном случае new-initializerинтерпретируется в соответствии с правилами инициализации [dcl.init] для прямой инициализации.

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

Если new-expressionсоздает объект или массив объектов типа класса, контроль доступа и неоднозначности выполняется для функции распределения, deallocation functionа также constructor. Если new-expression создает массив объектов типа класса, destructor потенциально вызывается.

Если какая-либо часть описанной выше инициализации объекта 80 завершается выдачей исключения и может быть найдена подходящая функция освобождения, вызывается функция освобождения, чтобы освободить память, в которой создавался объект, после чего исключение продолжает распространяться в контексте. из new-expression. Если не удается найти однозначно совпадающую функцию освобождения памяти, распространение исключения не приводит к освобождению памяти объекта. [ Note: Это уместно, когда вызываемая функция распределения не выделяет память; в противном случае это может привести к утечке памяти. ]end note

Если new-expressionначинается с унарного ​::​ оператора, имя функции освобождения ищется в глобальной области видимости. В противном случае, если выделенный тип является типом класса T или его массивом, имя функции освобождения ищется в области видимости T. Если этот поиск не может найти имя или если выделенный тип не является типом класса или его массивом, имя функции освобождения ищется в глобальной области.

Объявление функции освобождения размещения соответствует объявлению функции распределения размещения, если она имеет такое же количество параметров и после преобразования параметров ( [dcl.fct]) все типы параметров, кроме первого, идентичны. Если поиск находит единственную подходящую функцию освобождения, будет вызвана эта функция; в противном случае функция освобождения памяти не будет вызвана. Если поиск находит a usual deallocation function с параметром типа std​::​size_­t и эта функция, рассматриваемая как функция освобождения размещения, была бы выбрана в качестве соответствия для функции распределения, программа имеет неправильный формат. Для функции распределения, не связанной с размещением, обычный поиск функции освобождения используется для поиска соответствующей функции освобождения ( [expr.delete]) [Example:

struct S {
  // Placement allocation function:
  static void* operator new(std::size_t, std::size_t);

  // Usual (non-placement) deallocation function:
  static void operator delete(void*, std::size_t);
};

S* p = new (0) S;   // ill-formed: non-placement deallocation function matches
                    // placement allocation function

end example]

Если a new-expressionвызывает функцию освобождения, она передает значение, возвращенное при вызове функции распределения, в качестве первого аргумента типа void*. Если вызывается функция освобождения размещения, ей передаются те же дополнительные аргументы, которые были переданы функции распределения размещения, то есть те же аргументы, что и те, которые указаны в new-placementсинтаксисе. Если реализации разрешено делать копию любого аргумента как часть вызова функции распределения, разрешается делать копию (того же исходного значения) как часть вызова функции освобождения или повторно использовать копию сделано как часть вызова функции распределения. Если копия пропущена в одном месте, нет необходимости опускать ее в другом.

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

Это может включать оценку new-initializerи / или вызов конструктора.

8.3.5 Delete [expr.delete]

delete-expressionОператор разрушает most derived object или массив , созданный new-expression.

delete-expression:
	::opt delete cast-expression
	::opt delete [ ] cast-expression

Первый вариант предназначен для объектов, не являющихся массивами, а второй - для массивов. Всякий раз, когда за delete ключевым словом сразу следуют пустые квадратные скобки, оно должно интерпретироваться как вторая альтернатива. 81 Операнд должен иметь указатель на тип объекта или тип класса. Если тип класса, операнд contextually implicitly converted указывает на тип объекта. 82 В delete-expressionрезультате «S имеет тип void.

Если операнд имеет тип класса, операнд преобразуется в тип указателя путем вызова вышеупомянутой функции преобразования, и преобразованный операнд используется вместо исходного операнда в оставшейся части этого раздела. В первой альтернативе ( delete object) значением операнда delete может быть значение нулевого указателя, указатель на объект, не являющийся массивом, созданный предыдущим new-expression, или указатель на, subobject представляющий base class такой объект. В противном случае поведение не определено. Во второй альтернативе ( delete array) значение операнда delete может быть значением нулевого указателя или значением указателя, полученным в результате предыдущего массива new-expression. 83 В противном случае поведение не определено. [ Note: Это означает, что синтаксис delete-expressionдолжен соответствовать типу выделенного объекта new, а не синтаксису new-expression. ] [ Указатель на тип может быть операндом a ; в выражении указателя нет необходимости перед его использованием в качестве операнда . ]end noteNote: const delete-expressioncast away the constnessdelete-expressionend note

В первой альтернативе ( delete object), если статический тип удаляемого объекта отличается от его динамического типа, статический тип должен быть базовым классом динамического типа удаляемого объекта, а статический тип должен иметь виртуальный тип. деструктор или поведение не определено. Во второй альтернативе ( delete array), если динамический тип удаляемого объекта отличается от его статического типа, поведение не определено.

cast-expressionВ delete-expressionдолжна быть оценена только один раз.

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

Если значение операнда delete-expressionне является значением нулевого указателя, delete-expressionон вызовет деструктор (если есть) для объекта или элементов удаляемого массива. В случае с массивом элементы будут уничтожены в порядке убывания адреса (то есть в порядке, обратном завершению их конструктора; см. [class.base.init]).

Если значение операнда delete-expressionне является значением нулевого указателя, то:

  • Если вызов выделения для new-expressionудаляемого объекта не был пропущен и выделение не было расширено ( [expr.new]), delete-expressionобъект должен вызвать a deallocation function. Значение, возвращаемое из вызова распределения, new-expressionдолжно быть передано в качестве первого аргумента функции освобождения.

  • В противном случае, если выделение было расширено или было предоставлено путем расширения выделения другого new-expression, и delete-expressionдля каждого другого значения указателя, созданного a, new-expressionкоторый имел хранилище, предоставленное расширенным new-expression, было оценено, то delete-expressionдолжен вызвать функцию освобождения. Значение, возвращаемое из вызова распределения расширенного, new-expression должно быть передано в качестве первого аргумента функции освобождения.

  • В противном случае delete-expressionфункция освобождения памяти не будет вызвана.

[ Note: Функция освобождения вызывается независимо от того, генерирует ли деструктор для объекта или какого-либо элемента массива исключение. ] Если значение операнда является значением нулевого указателя, не указано, будет ли вызываться функция освобождения, как описано выше.end notedelete-expression

[ Note: Реализация предоставляет определения по умолчанию operator delete для функций глобального освобождения для не-массивов ( [new.delete.single]) и operator delete[] для массивов ( [new.delete.array]). Программа на C ++ может предоставлять альтернативные определения этих функций ( [replacement.functions]) и / или версии для конкретных классов ( [class.free]). ]end note

Когда ключевому слову delete в a delete-expressionпредшествует унарный ​::​ оператор, имя функции освобождения ищется в глобальной области. В противном случае поиск учитывает функции освобождения, специфичные для класса ( [class.free]). Если не найдена зависящая от класса функция освобождения, имя функции освобождения ищется в глобальной области видимости.

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

  • Если тип имеет выравнивание "новый-расширенный", std​::​align_­val_­t предпочтительнее использовать функцию с параметром типа ; в противном случае предпочтительнее использовать функцию без такого параметра. Если найдена ровно одна предпочтительная функция, выбирается эта функция, и процесс выбора завершается. Если найдено более одной предпочтительной функции, все нежелательные функции исключаются из дальнейшего рассмотрения.

  • Если функции освобождения имеют область видимости класса, std​::​size_­t выбирается функция без параметра типа .

  • Если тип является полным и если только для второй альтернативы (удалить массив), операнд является указателем на тип класса с нетривиальным деструктором или его (возможно, многомерным) массивом, функция с параметром тип std​::​size_­t выбран.

  • В противном случае не указано, std​::​size_­t выбрана ли функция освобождения с параметром типа .

Когда delete-expression выполняется a , выбранная функция освобождения должна вызываться с адресом наиболее производного объекта в delete object случае или адресом объекта, соответствующим образом скорректированным с учетом накладных расходов на выделение массива ( [expr.new]) в delete array случае, в качестве ее первого аргумента. Если используется функция освобождения с параметром типа std​::​align_­val_­t , выравнивание типа удаляемого объекта передается как соответствующий аргумент. Если используется функция освобождения с параметром типа std​::​size_­t , размер наиболее производного типа или массива плюс накладные расходы на выделение соответственно передается в качестве соответствующего аргумента. 84 [ Note: Если это приводит к вызову обычной функции освобождения памяти, и либо первый аргумент не был результатом предыдущего вызова обычной функции распределения, либо второй аргумент не был соответствующим аргументом в указанном вызове, поведение не определено ( [new.delete.single], [new.delete.array]). ]end note

Контроль доступа и неоднозначности выполняется как для функции освобождения, так и для деструктора ( [class.dtor], [class.free]).

Лямбда-выражение lambda-introducer, состоящее из пустых квадратных скобок, может следовать за delete ключевым словом, если лямбда-выражение заключено в круглые скобки.

Это означает, что объект нельзя удалить с помощью указателя типа, void* поскольку void он не является типом объекта.

Для массивов ненулевой длины это то же самое, что указатель на первый элемент массива, созданного им new-expression. Массивы нулевой длины не имеют первого элемента.

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

8.3.6 Alignof [expr.alignof]

alignof Выражение дает требование выравнивания его типа операнда. Операнд должен type-id представлять собой полный тип объекта или его массив, или ссылку на один из этих типов.

Результат - интегральная константа типа std​::​size_­t.

Когда alignof применяется к ссылочному типу, результатом является выравнивание ссылочного типа. Когда alignof применяется к типу массива, результатом является выравнивание типа элемента.

8.3.7 noexcept operator [expr.unary.noexcept]

noexcept Оператор определяет , является ли оценка ее операнда, который является unevaluated operand, может throw an exception.

noexcept-expression:
	noexcept ( expression )

Результатом noexcept оператора является константа типа bool и prvalue.

Результатом noexcept оператора является, true если только он не expressionявляется potentially-throwing.

8.4 Explicit type conversion (cast notation) [expr.cast]

Результат выражения имеет тип . Результатом является lvalue, если это ссылочный тип lvalue, или ссылка rvalue на тип функции и xvalue, если ссылка rvalue на тип объекта; в противном случае результатом будет prvalue. [ Если это неклассовый тип, квалифицированный cv, при определении типа результирующего prvalue отбрасываются; см. пункт . ] (T) cast-expression T T TNote: T cv-qualifiers [expr]end note

Явное преобразование типа может быть выражено с использованием functional notation, оператор преобразования типа ( dynamic_­cast, static_­cast, reinterpret_­cast, const_­cast), или cast обозначения.

cast-expression:
	unary-expression
	( type-id ) cast-expression

Любое преобразование типа, не упомянутое ниже и явно не определенное пользователем ( [class.conv]), является некорректным.

Преобразования, выполненные

может выполняться с использованием нотации приведения явного преобразования типа. Применяются те же семантические ограничения и поведение, за исключением того, что при выполнении a static_­cast в следующих ситуациях преобразование допустимо, даже если базовый класс недоступен:

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

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

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

Если преобразование можно интерпретировать более чем одним из способов, перечисленных выше, используется интерпретация, которая появляется первой в списке, даже если приведение в результате этой интерпретации неверно. Если преобразование может быть интерпретировано более чем одним способом как знак, static_­cast за которым следует знак const_­cast, преобразование сформировано неправильно. [Example:

struct A { };
struct I1 : A { };
struct I2 : A { };
struct D : I1, I2 { };
A* foo( D* p ) {
  return (A*)( p );             // ill-formed static_­cast interpretation
}

end example]

Операндом приведения с использованием нотации приведения может быть значение типа «указатель на неполный тип класса». Целевым типом приведения с использованием нотации приведения может быть «указатель на неполный тип класса». Если и операнд, и тип назначения являются типами классов и один или оба являются неполными, не указывается, используется ли интерпретация static_­cast или reinterpret_­cast интерпретация, даже если между двумя классами существует отношение наследования. [ Note: Например, если классы были определены позже в единице перевода, многопроходному компилятору было бы разрешено интерпретировать приведение между указателями на классы, как если бы типы классов были завершены в точке приведения. ]end note

8.5 Pointer-to-member operators [expr.mptr.oper]

Операторы указателя на член ->* и .* группировка слева направо.

pm-expression:
	cast-expression
	pm-expression .* cast-expression
	pm-expression ->* cast-expression

Бинарный оператор .* связывает свой второй операнд, который должен иметь тип «указатель на член T», со своим первым операндом, который должен быть значением gl класса T или класса, T однозначным и доступным базовым классом. Результатом является объект или функция типа, указанного вторым операндом.

Бинарный оператор ->* связывает свой второй операнд, который должен иметь тип «указатель на член T», со своим первым операндом, который должен иметь тип «указатель на U», где U либо T один из классов, либо класс которого T является однозначным и доступным базовым классом. Выражение E1->*E2 преобразуется в эквивалентную форму (*(E1)).*E2.

Сокращение как , называется . Если динамический тип не содержит члена, на который ссылается, поведение не определено. В противном случае выражение ставится перед выражением .pm-expression.*cast-expression E1.*E2 E1 object expression E1 E2 E1 E2

Ограничения на cv-квалификацию и способ, которым cv-квалификаторы операндов комбинируются для получения cv-квалификаторов результата, такие же, как правила для E1.E2 данных в [expr.ref]. [ Note: Невозможно использовать указатель на член, который ссылается на mutable член, для изменения const объекта класса. Например,

struct S {
  S() : i(0) { }
  mutable int i;
};
void f()
{
  const S cs;
  int S::* pm = &S::i;          // pm refers to mutable member S​::​i
  cs.*pm = 88;                  // ill-formed: cs is a const object
}

end note]

Если результатом .* или ->* является функция, то этот результат можно использовать только как операнд для оператора вызова функции (). [Example:

(ptr_to_obj->*ptr_to_mfct)(10);

вызывает функцию-член, обозначенную ptr_­to_­mfct для объекта, на который указывает ptr_­to_­obj. ] В выражении, объектное выражение которого является rvalue, программа имеет неправильный формат, если второй операнд является указателем на функцию-член с . В выражении, объектное выражение которого является lvalue, программа имеет неправильный формат, если второй операнд является указателем на функцию-член с . Результатом выражения, второй операнд которого является указателем на член данных, является lvalue, если первый операнд - lvalue, и xvalue в противном случае. Результатом выражения, второй операнд которого является указателем на функцию-член, является prvalue. Если второй операнд - это , поведение не определено.end example .* ref-qualifier & .*ref-qualifier && .* .* null member pointer value

8.6 Multiplicative operators [expr.mul]

Мультипликативные операторы *, /и % группируются слева направо.

multiplicative-expression:
	pm-expression
	multiplicative-expression * pm-expression
	multiplicative-expression / pm-expression
	multiplicative-expression % pm-expression

Операнды * и / должны иметь арифметический или незаданный тип перечисления; операнды % должны иметь целочисленный или незадействованный перечислимый тип. Обычные арифметические преобразования выполняются с операндами и определяют тип результата.

Бинарный * оператор указывает на умножение.

Бинарный / оператор возвращает частное, а бинарный % оператор возвращает остаток от деления первого выражения на второе. Если второй операнд / или % равен нулю, поведение не определено. Для целых операндов / оператор дает алгебраическое частное с отброшенной дробной частью; 85 если частное a/b представимо в типе результата, (a/b)*b + a%b равно a; в противном случае поведение обоих a/b и a%b не определено.

Это часто называют усечением до нуля.

8.7 Additive operators [expr.add]

Аддитивные операторы + и - группа слева направо. Обычные арифметические преобразования выполняются для операндов арифметического или перечислительного типа.

additive-expression:
	multiplicative-expression
	additive-expression + multiplicative-expression
	additive-expression - multiplicative-expression

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

Для вычитания должно выполняться одно из следующих условий:

  • оба операнда имеют арифметический тип или тип перечисления без области действия; или

  • оба операнда являются указателями на cv-определенные или cv-неквалифицированные версии одного и того же полностью определенного типа объекта; или

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

Результатом бинарного + оператора является сумма операндов. Результатом бинарного - оператора является разница, полученная в результате вычитания второго операнда из первого.

Когда выражение, имеющее целочисленный тип, добавляется к указателю или вычитается из него, результат имеет тип операнда указателя. Если выражение P указывает на элемент x[i] объекта массива x с n элементами, 86 выражения P + J и J + P (где J имеет значение j) указывают на (возможно, гипотетический) элемент x[i+j] if 0i+jn; в противном случае поведение не определено. Точно так же выражение P - J указывает на (возможно, гипотетический) элемент x[ij] if 0ijn; в противном случае поведение не определено.

Когда вычитаются два указателя на элементы одного и того же объекта массива, тип результата является определяемым реализацией целочисленным типом со знаком; этот тип должен быть того же типа, который определен std​::​ptrdiff_­t в <cstddef> заголовке ( [support.types]). Если выражения P и Q указывают на элементы x[i] и x[j] одного и того же объекта массива x, соответственно, выражение P - Q имеет значение ij; в противном случае поведение не определено. [ Note: Если значение ij не входит в диапазон представимых значений типа std​::​ptrdiff_­t, поведение не определено. ]end note

Для сложения или вычитания, если выражения P или Q имеют тип «указатель на cv T», где T и тип элемента массива нет similar, поведение не определено. [ Note: В частности, указатель на базовый класс не может использоваться для арифметики указателей, когда массив содержит объекты типа производного класса. ]end note

Если значение 0 добавляется к значению нулевого указателя или вычитается из него, результатом является значение нулевого указателя. Если вычитаются два значения нулевого указателя, результат сравнивается со значением 0, преобразованным в тип std​::​ptrdiff_­t.

Для этой цели объект, не являющийся элементом массива, считается принадлежащим к одноэлементному массиву; см [expr.unary.op]. Указатель последнего элемента массива x из n элементов считаются эквивалентным указателем на гипотетическом элемент x[n] для этой цели; см [basic.compound].

8.8 Shift operators [expr.shift]

Операторы сдвига << и >> группировка слева направо.

shift-expression:
	additive-expression
	shift-expression << additive-expression
	shift-expression >> additive-expression

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

Значение E1 << E2 является E1 сдвинуты влево E2 позиции битов; освобожденные биты заполняются нулями. Если E1 имеет тип без знака, значение результата будет E1×2E2уменьшено по модулю на единицу больше, чем максимальное значение, представленное в типе результата. В противном случае, если он E1 имеет знаковый тип и неотрицательное значение и E1×2E2 может быть представлен в соответствующем беззнаковом типе типа результата, то это значение, преобразованное в тип результата, является результирующим значением; в противном случае поведение не определено.

Значение E1 >> E2 является E1 правой сдвинутые E2 битовые позиции. Если E1 имеет тип без знака или если E1 имеет тип со знаком и неотрицательное значение, значение результата является неотъемлемой частью частного от E1/2E2. Если имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией. E1

Выражение E1 упорядочивается перед выражением E2.

8.9 Relational operators [expr.rel]

Операторы отношения группируются слева направо. [ Example: a<b<c значит (a<b)<c и нет (a<b)&&(b<c) . ]end example

relational-expression:
	shift-expression
	relational-expression < shift-expression
	relational-expression > shift-expression
	relational-expression <= shift-expression
	relational-expression >= shift-expression

Операнды должны иметь арифметический, перечислительный или указательный тип. Операторы < (меньше), > (больше), <= (меньше или равно) и >= (больше или равно) все дают false или true. Тип результата bool.

Обычные арифметические преобразования выполняются для операндов арифметического или перечислительного типа. Если оба операнда являются указателями pointer conversions и qualification conversions выполняются, чтобы привести их к своим composite pointer type. После преобразований операнды должны иметь один и тот же тип.

Сравнение неравных указателей на объекты 87 определяется следующим образом:

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

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

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

Если два операнда p и q compare equal, p<=q и p>=q оба дают true и, p<q и p>q оба дают false. В противном случае, если указатель p сравнивает больше , чем указатель q, p>=q, p>q, q<=p, и q<p весь урожай true и p<=q, p<q, q>=pи q>p общий выход false. В противном случае результат каждого из операторов не указан.

Если оба операнда (после преобразований) относятся к арифметическому или перечислимому типу, каждый из операторов должен выдать результат, true если указанная связь истинна, а false если - ложна.

Для этой цели объект, не являющийся элементом массива, считается принадлежащим к одноэлементному массиву; см [expr.unary.op]. Указатель последнего элемента массива x из n элементов считаются эквивалентным указателем на гипотетическом элемент x[n] для этой цели; см [basic.compound].

8.10 Equality operators [expr.eq]

equality-expression:
	relational-expression
	equality-expression == relational-expression
	equality-expression != relational-expression

Операторы == (равно) и != (не равно) группируются слева направо. Операнды должны иметь арифметику, перечисление, указатель или указатель на тип или тип члена std​::​nullptr_­t. Операторы == и != оба дают результат true или false, т. Е. Результат типа bool. В каждом из приведенных ниже случаев операнды должны иметь один и тот же тип после применения указанных преобразований.

Если хотя бы один из операндов является указателем, pointer conversions, function pointer conversionsи qualification conversions выполняются на обоих операндов , чтобы привести их в свои composite pointer type. Сравнение указателей определяется следующим образом:

  • Если один указатель представляет адрес полного объекта, а другой указатель представляет адрес, следующий за последним элементом другого полного объекта, 88 результат сравнения не указан.

  • В противном случае, если оба указателя равны нулю, оба указывают на одну и ту же функцию или на обе represent the same address, они сравниваются одинаково.

  • В противном случае указатели сравниваются неравно.

Если хотя бы один из операндов является указателем на член pointer to member conversions и qualification conversions выполняется для обоих операндов, чтобы привести их к их composite pointer type. Сравнение указателей на члены определяется следующим образом:

  • Если два указателя на члены являются значениями нулевого указателя на член, они сравниваются как равные.

  • Если только один из двух указателей на члены является значением нулевого указателя на член, они сравниваются неравно.

  • Если любой из них является указателем на виртуальную функцию-член, результат не указан.

  • Если один относится к члену класса, C1 а другой - к члену другого класса C2, где ни один из них не является базовым классом другого, результат не указан. [Example:

    struct A {};
    struct B : A { int x; };
    struct C : A { int x; };
    
    int A::*bx = (int(A::*))&B::x;
    int A::*cx = (int(A::*))&C::x;
    
    bool b1 = (bx == cx);   // unspecified
    

    end example]

  • Если оба относятся к (возможно, различным) членам одного и того же union, они сравниваются как равные.

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

    struct B {
      int f();
    };
    struct L : B { };
    struct R : B { };
    struct D : L, R { };
    
    int (B::*pb)() = &B::f;
    int (L::*pl)() = pb;
    int (R::*pr)() = pb;
    int (D::*pdl)() = pl;
    int (D::*pdr)() = pr;
    bool x = (pdl == pdr);          // false
    bool y = (pb == pl);            // true
    

    end example]

Два операнда типа std​::​nullptr_­t или один операнд типа std​::​nullptr_­t и другой константа нулевого указателя сравниваются равными.

Если два операнда сравниваются равными, результат true для == оператора и false для != оператора. Если два операнда не равны, результат будет false для == оператора и true для != оператора. В противном случае результат каждого из операторов не указан.

Если оба операнда относятся к арифметическому или перечислимому типу, обычные арифметические преобразования выполняются для обоих операндов; каждый из операторов должен уступить, true если указанное отношение истинно и false если оно ложно.

Для этой цели объект, не являющийся элементом массива, считается принадлежащим к одноэлементному массиву; см [expr.unary.op].

8.11 Bitwise AND operator [expr.bit.and]

and-expression:
	equality-expression
	and-expression & equality-expression

Выполняются обычные арифметические преобразования; результатом является побитовая функция И для операндов. Оператор применяется только к целым операндам перечисления или операндам без области действия.

8.12 Bitwise exclusive OR operator [expr.xor]

exclusive-or-expression:
	and-expression
	exclusive-or-expression ^ and-expression

Выполняются обычные арифметические преобразования; результатом является побитовая функция исключающего ИЛИ операндов. Оператор применяется только к целым операндам перечисления или операндам без области действия.

8.13 Bitwise inclusive OR operator [expr.or]

inclusive-or-expression:
	exclusive-or-expression
	inclusive-or-expression | exclusive-or-expression

Выполняются обычные арифметические преобразования; результатом является побитовая инклюзивная функция ИЛИ его операндов. Оператор применяется только к целым операндам перечисления или операндам без области действия.

8.14 Logical AND operator [expr.log.and]

logical-and-expression:
	inclusive-or-expression
	logical-and-expression && inclusive-or-expression

В && операторных группах слева направо. Оба операнда contextually converted to bool. Результат - true если оба операнда - true и в false противном случае. В отличие от &, && гарантирует оценку слева направо: второй операнд не оценивается, если первый операнд false.

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

8.15 Logical OR operator [expr.log.or]

logical-or-expression:
	logical-and-expression
	logical-or-expression || logical-and-expression

В || операторных группах слева направо. Оба операнда contextually converted to bool. Он возвращается, true если какой-либо из его операндов равен true, и в false противном случае. В отличие от |, || гарантирует оценку слева направо; более того, второй операнд не оценивается, если первый операнд оценивается как true.

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

8.16 Conditional operator [expr.cond]

conditional-expression:
	logical-or-expression
	logical-or-expression ? expression : assignment-expression

Условные выражения группируются справа налево. Первое выражение contextually converted to bool. Он оценивается, и если это так true, результатом условного выражения является значение второго выражения, в противном случае - значение третьего выражения. Оценивается только одно из второго и третьего выражений. Каждое вычисление значения и побочный эффект, связанный с первым выражением, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным со вторым или третьим выражением.

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

  • Второй или третий операнд (но не оба) - это (возможно, в скобках) throw-expression; результат относится к типу и категории ценности другого. Это conditional-expression битовое поле, если этот операнд является битовым полем.

  • И второй, и третий операнды имеют тип void; результат имеет тип void и является значением prvalue. [ Note: Это включает случай, когда оба операнда throw-expressions. ] end note

В противном случае, если второй и третий операнды являются битовыми полями glvalue той же категории значений и типов cv1 T и cv2 T, соответственно, операнды считаются относящимися к типу cv T для оставшейся части этого раздела, где cv является объединением cv1 и cv2.

В противном случае, если второй и третий операнды имеют разные типы и либо имеют (возможно, cv-квалификационный) тип класса, либо если оба являются значениями gl с одной и той же категорией значений и одним и тем же типом, за исключением cv-квалификации, делается попытка сформировать implicit conversion sequence от каждого из этих операндов к типу другого. [ Note: Такие свойства, как доступ, является ли операнд битовым полем или удалена ли функция преобразования, при этом определении игнорируются. ] Предпринимаются попытки сформировать неявную последовательность преобразования из выражения операнда типа в целевой тип, связанный с типом выражения операнда, следующим образом:end note E1 T1 T2 E2

  • Если E2 - lvalue, то целевой тип - «lvalue ссылка на T2» с учетом ограничения, что при преобразовании ссылка должна bind directly быть на lvalue.

  • Если E2 - xvalue, целевой тип - «ссылка rvalue на T2» с учетом ограничения, которое ссылка должна связывать напрямую.

  • Если E2 это prvalue или если ни одна из приведенных выше последовательностей преобразования не может быть сформирована, и хотя бы один из операндов имеет (возможно, cv-квалифицируемый) тип класса:

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

    • в противном случае, целевой тип является типом , который E2 будет иметь после применения lvalue-to-rvalue, array-to-pointerи function-to-pointer стандартных преобразований.

Используя этот процесс, определяется, может ли неявная последовательность преобразования быть сформирована из второго операнда в целевой тип, определенный для третьего операнда, и наоборот. Если могут быть сформированы обе последовательности или одна может быть сформирована, но это неоднозначная последовательность преобразования, программа сформирована неправильно. Если последовательность преобразования не может быть сформирована, операнды остаются неизменными, и выполняется дальнейшая проверка, как описано ниже. В противном случае, если может быть сформирована ровно одна последовательность преобразования, это преобразование применяется к выбранному операнду, а преобразованный операнд используется вместо исходного операнда для оставшейся части этого раздела. [ Note: Преобразование может быть неправильно сформировано, даже если может быть сформирована неявная последовательность преобразования. ]end note

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

В противном случае результатом будет prvalue. Если второй и третий операнды не имеют одного и того же типа и оба имеют (возможно, cv-квалифицируемый) тип класса, разрешение перегрузки используется для определения преобразований (если они есть), которые будут применяться к операндам ( [over.match.oper], [over.built]). Если разрешение перегрузки не удается, программа имеет неправильный формат. В противном случае применяются определенные таким образом преобразования, а преобразованные операнды используются вместо исходных операндов в оставшейся части этого раздела.

Lvalue-to-rvalue,, array-to-pointerи function-to-pointer стандартные преобразования выполняются для второго и третьего операндов. После этих преобразований выполняется одно из следующих действий:

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

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

  • Один или оба из второго и третьего операндов имеют тип указателя; pointer conversions, function pointer conversionsИ qualification conversions выполнены , чтобы привести их к композитному типу указателя (пункт [expr]). Результатом является тип составного указателя.

  • Один или оба из второго и третьего операндов имеют указатель на тип элемента; pointer to member conversions и qualification conversions выполняются, чтобы довести их до своего composite pointer type. Результатом является тип составного указателя.

  • И второй, и третий операнды имеют тип std​::​nullptr_­t или один имеет этот тип, а другой является константой нулевого указателя. Результат типа std​::​nullptr_­t.

8.17 Throwing an exception [expr.throw]

throw-expression:
	throw  assignment-expressionopt

А throw-expressionимеет тип void.

Вычисление throw-expressionс операндом throws an exception; Тип объекта исключения определяется удалением любого верхнего уровня cv-qualifiers из статического типа операнда и изменением типа с «массив T» или типа функции T на «указатель на T».

A throw-expression без операнда повторно генерирует currently handled exception. Исключение повторно активируется существующим объектом исключения; новый объект исключения не создается. Исключение больше не считается обнаруженным. [ Example: Код, который должен быть выполнен из-за исключения, но не может полностью обработать само исключение, можно записать следующим образом:

try {
    // ...
} catch (...) {     // catch all exceptions
  // respond (partially) to exception
  throw;            // pass the exception to some other handler
}

end example]

Если в настоящее время не обрабатывается никаких исключений, выполняется оценка throw-expression без вызовов операндов std​::​​terminate().

8.18 Assignment and compound assignment operators [expr.ass]

Оператор присваивания ( =) и составные операторы присваивания группируются справа налево. Все требуют изменяемого lvalue в качестве левого операнда и возвращают lvalue, относящееся к левому операнду. Результатом во всех случаях является битовое поле, если левый операнд является битовым полем. Во всех случаях присваивание выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания. Правый операнд ставится перед левым операндом. Что касается вызова функции с неопределенной последовательностью, операция составного присваивания является однократной оценкой. [ Note: Следовательно, вызов функции не должен вмешиваться между преобразованием lvalue-to-rvalue и побочным эффектом, связанным с каким-либо одним составным оператором присваивания. ]end note

assignment-expression:
	conditional-expression
	logical-or-expression assignment-operator initializer-clause
	throw-expression
assignment-operator: one of
	=  *=  /=  %=   +=  -=  >>=  <<=  &=  ^=  |=

В простом присваивании ( =) значение выражения заменяет значение объекта, на который ссылается левый операнд.

Если левый операнд не относится к типу класса, выражение неявно преобразуется (Clause [conv]) в тип cv-unqualified левого операнда.

Если левый операнд относится к типу класса, класс должен быть полным. Присвоение объектам класса определяется оператором присваивания копировать / перемещать ( [class.copy], [over.ass]).

[ Note: Для объектов класса, назначение не в общем такой же , как инициализации ( [dcl.init], [class.ctor], [class.init], [class.copy]). ]end note

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

Поведение выражения формы E1 op= E2 эквивалентно, за E1 = E1 op E2 исключением того, что E1 оценивается только один раз. В += и -=, E1 должен либо иметь арифметический тип или быть указателем на полностью определенный тип объекта , возможно , CV-квалификации. Во всех остальных случаях E1 должен иметь арифметический тип.

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

С braced-init-listправой стороны от

  • присвоение скаляру, и в этом случае список инициализаторов должен иметь не более одного элемента. Значение x = {v}, где T - скалярный тип выражения x, - значение x = T{v}. Смысл в x = {} том x = T{}.

  • присваивание объекту типа класса, и в этом случае список инициализаторов передается в качестве аргумента функции оператора присваивания, выбранной с помощью разрешения перегрузки ( [over.ass], [over.match]).

[Example:

complex<double> z;
z = { 1,2 };              // meaning z.operator=({1,2})
z += { 1, 2 };            // meaning z.operator+=({1,2})
int a, b;
a = b = { 1 };            // meaning a=b=1;
a = { 1 } = b;            // syntax error

end example]

8.19 Comma operator [expr.comma]

Группа операторов запятой группируется слева направо.

expression:
	assignment-expression
	expression , assignment-expression

Пара выражений, разделенных запятой, оценивается слева направо; левое выражение - это discarded-value expression. Каждое вычисление значения и побочный эффект, связанный с левым выражением, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным с правым выражением. Тип и значение результата - это тип и значение правого операнда; результат имеет ту же категорию значений, что и его правый операнд, и является битовым полем, если его правый операнд является битовым полем. Если правый операнд - временное выражение ( [class.temporary]), результатом будет временное выражение.

В контекстах, где запятой придается особое значение, [ Example: в списках аргументов функций ( [expr.call]) и списках инициализаторов ( [dcl.init]) ] оператор запятой, как описано в разделе, может появляться только в круглых скобках. [ end example [expr] Example:

f(a, (t=3, t+2), c);

имеет три аргумента, второй из которых имеет значение 5. ]end example

8.20 Constant expressions [expr.const]

В некоторых контекстах требуются выражения, удовлетворяющие дополнительным требованиям, подробно описанным в этом подпункте; другие контексты имеют разную семантику в зависимости от того, удовлетворяет ли выражение этим требованиям. Вызываются выражения, которые удовлетворяют этим требованиям, предполагая, что выполняется копирование constant expressions. [ Note: Постоянные выражения можно оценивать во время перевода. ]end note

constant-expression:
	conditional-expression

Выражение e является a, core constant expression если оценка e, следуя правилам abstract machine, будет оценивать одно из следующих выражений:

  • this, за исключением функции constexpr или конструктора constexpr, который оценивается как часть e;

  • вызов функции, отличной от конструктора constexpr для литерального класса, функции constexpr или неявный вызов тривиального деструктора ( [class.dtor]) [ Note: Overload resolution применяется как обычно ]; end note

  • вызов неопределенной функции constexpr или неопределенного конструктора constexpr;

  • вызов созданной функции constexpr или конструктора constexpr, который не удовлетворяет требованиям для функции или constexpr конструктора constexpr ;

  • выражение, которое превзойдет implementation-defined limits;

  • операция , которая будет иметь неопределенное поведение , как указано в пунктах [intro] через [cpp] настоящий стандарт [ в Note: том числе, например, целое число со знаком переполнение (раздел [expr]), определенный указателем арифметического ( [expr.add]), division by zeroили определенным ]; shift operations end note

  • lvalue-to-rvalue conversion , если она не применяется к

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

    • энергонезависимое значение glvalue, которое относится к подобъекту a string literal, или

    • энергонезависимое значение glvalue, которое относится к энергонезависимому объекту, определенному с помощью constexpr, или которое относится к неизменяемому подобъекту такого объекта, или

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

  • объект, lvalue-to-rvalue conversion который применяется к значению glvalue, которое относится к неактивному члену объединения или его подобъекту;

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

  • assignment expression или вызов оператора присваивания ( [class.copy]) , который изменит активный член союза;

  • , id-expressionкоторый относится к переменной или элементу данных ссылочного типа, если ссылка не имеет предшествующей инициализации и либо

    • он инициализируется постоянным выражением или

    • его время жизни началось в пределах оценки e;

  • в a lambda-expression- ссылка на this или на переменную с автоматической продолжительностью хранения, определенной вне этого lambda-expression, где ссылка будет odr-use; [Example:

    void g() {
      const int n = 0;
      [=] {
        constexpr int i = n;   // OK, n is not odr-used and not captured here
        constexpr int j = *&n; // ill-formed, &n would be an odr-use of n
      };
    }

    end example] [ Note: Если odr-use происходит при вызове оператора вызова функции закрытого типа, оно больше не ссылается this на включающую автоматическую переменную из-за преобразования ( [expr.prim.lambda.capture]) id-expressionв доступ к соответствующему члену данных. [Example:

    auto monad = [](auto v) { return [=] { return v; }; };
    auto bind = [](auto m) {
      return [=](auto fvm) { return fvm(m()); };
    };
    
    // OK to have captures to automatic objects created during constant expression evaluation.
    static_assert(bind(monad(2))(monad)() == monad(2)());

    end example] ]end note

  • преобразование типа cv void* в тип указателя на объект;

  • а dynamic cast;

  • а reinterpret_­cast;

  • а pseudo-destructor call;

  • модификация объекта ( [expr.ass], [expr.post.incr], [expr.pre.incr]) , если оно не применяются к энергонезависимому именующему буквальному типу , который ссылается на энергонезависимый объект , чей срок служба начался в оценке e;

  • a typeid expression , операнд которого является значением glvalue типа полиморфного класса;

  • а new-expression;

  • а delete-expression;

  • relational или equality оператора , где результат не определен; или

  • а throw-expression.

Если e удовлетворяют ограничения выражения основного постоянная, но Оценочное e будут оценивать операцию , которая имеет неопределенное поведение , как указано в пунктах [library] через [thread] настоящий международный стандарт, это не определенно ли e это выражение ядра постоянная.

[Example:

int x;                              // not constant
struct A {
  constexpr A(bool b) : m(b?42:x) { }
  int m;
};
constexpr int v = A(true).m;        // OK: constructor call initializes m with the value 42

constexpr int w = A(false).m;       // error: initializer for m is x, which is non-constant

constexpr int f1(int k) {
  constexpr int x = k;              // error: x is not initialized by a constant expression
                                    // because lifetime of k began outside the initializer of x
  return x;
}
constexpr int f2(int k) {
  int x = k;                        // OK: not required to be a constant expression
                                    // because x is not constexpr
  return x;
}

constexpr int incr(int &n) {
  return ++n;
}
constexpr int g(int k) {
  constexpr int x = incr(k);        // error: incr(k) is not a core constant expression
                                    // because lifetime of k began outside the expression incr(k)
  return x;
}
constexpr int h(int k) {
  int x = incr(k);                  // OK: incr(k) is not required to be a core constant expression
  return x;
}
constexpr int y = h(1);             // OK: initializes y with the value 2
                                    // h(1) is a core constant expression because
                                    // the lifetime of k begins inside h(1)

end example]

An integral constant expression - это выражение целочисленного типа или типа перечисления с незаданной областью, неявно преобразованное в prvalue, где преобразованное выражение является основным постоянным выражением. [ Note: Такие выражения могут использоваться как bit-field длины, как инициализаторы перечислителя, если базовый тип не является фиксированным ( [dcl.enum]), и как alignments. ]end note

converted constant expression Типа T представляет собой выражение, неявно преобразован в тип T, где преобразованное выражение является выражением постоянная и неявное последовательность преобразования содержит только

и где привязка ссылки (если есть) привязывается напрямую. [ Note: Такие выражения могут использоваться new expressions, как case expressions, как инициализаторы перечислителя, если базовый тип fixed, как array границы, так и не тип template arguments. ] A - это выражение, где преобразованное выражение является постоянным выражением, а последовательность преобразования содержит только указанные выше преобразования.end note contextually converted constant expression of type bool contextually converted to bool

A constant expression - это либо основное постоянное выражение glvalue, которое относится к сущности, которая является разрешенным результатом константного выражения (как определено ниже), либо основное постоянное выражение prvalue, значение которого удовлетворяет следующим ограничениям:

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

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

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

Сущность - permitted result of a constant expression это объект со статической продолжительностью хранения, который либо не является временным объектом, либо является временным объектом, значение которого удовлетворяет указанным выше ограничениям, либо это функция.

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

bool f() {
    char array[1 + int(1 + 0.2 - 0.1 - 0.1)];  // Must be evaluated during translation
    int size = 1 + int(1 + 0.2 - 0.1 - 0.1);   // May be evaluated at runtime
    return sizeof(array) == size;
}

Не указано, будет ли значение f() быть true или false. ] ]end example end note

Если выражение литерального типа класса используется в контексте, где требуется интегральное постоянное выражение, то это выражение относится contextually implicitly converted к целочисленному или незадействованному типу перечисления, и выбранная функция преобразования должна быть constexpr. [Example:

struct A {
  constexpr A(int i) : val(i) { }
  constexpr operator int() const { return val; }
  constexpr operator long() const { return 43; }
private:
  int val;
};
template<int> struct X { };
constexpr A a = 42;
X<a> x;             // OK: unique conversion to int
int ary[a];         // error: ambiguous conversion

end example]

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