8 Expressions [expr]

8.1 Primary expressions [expr.prim]

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(который также является типом закрывающего объекта) - это уникальный безымянный тип класса без объединения, называемый theclosure 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 alocal 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-usesthis (в случае объекта, обозначенного*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(см. Ниже). Неявное использование odrthis может привести к неявному захвату. ]end note

Сущность -captured это если она захвачена явно или неявно. Сущность, захваченная объектом, lambda-expressionнаходитсяodr-used в области, содержащей lambda-expression. Если*this он захвачен локальным лямбда-выражением, его ближайшая включающая функция должна быть нестатической функцией-членом. Если a lambda-expressionили создание экземпляра шаблона оператора вызова функции универсальной лямбда-выраженияodr-usesthis или переменной с автоматической продолжительностью хранения из достигаемой области действия, этот объект должен быть захвачен 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-expressionend 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-expressionm2 захватывает сущность, и эта сущность захватывается немедленным включением 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]