1

I can't find anything about throwing exceptions during constructing closure object.

It's oblivious that this expression can throw during copy construction of the vector:

auto v = std::vector<int>(1000000);
[v]{};

But what about empty or "by reference" capture lists like this:

[&]{};

I'm speaking only about construction of closure object now. Calling isn't interesting.

I read 5.1.2 Lambda expressions [expr.prim.lambda], but found nothing special about nothrow guarantee.

MasterAM
  • 16,283
  • 6
  • 45
  • 66
magras
  • 1,709
  • 21
  • 32

1 Answers1

5

According to the standard (draft n3337), §5.1.2/3:

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.

In a nutshell, a lambda expression ultimately instantiates an anonymous type (known only to the compiler), containing member variables that store the values indicated in the capture list. So imagine you saw a class that looked like this:

class __IgnoreMe__
{
    std::vector<int>& _v;

public:
    __IgnoreMe__(std::vector<int>& v)
        : _v(v)
    {
    }

    void operator()()
    {
    }
};

(Note: this isn't exactly how the class created by the compiler looks. The standard lays out specific requirements for generated classes, which I've left out for brevity's sake.)

Now imagine you instantiate that class like this:

auto v = std::vector<int>(1000000);
auto my_lambda = __IgnoreMe__(v);

Can the instantiation of my_lambda throw an exception? If so, then the construction of the closure object can throw an exception. (It can't, in this case.)

As for providing nothrow guarantees, the standard doesn't require compilers to account for this, but it doesn't prevent them from doing it either. The end of §5.1.2/3 states:

An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

— the size and/or alignment of the closure type,

— whether the closure type is trivially copyable (Clause 9),

— whether the closure type is a standard-layout class (Clause 9), or

— whether the closure type is a POD class (Clause 9).

Lilshieste
  • 2,744
  • 14
  • 20
  • 1
    Note that a lambda doesn't *need* to have reference members. Some implementations just use a single pointer to a stack frame instead of N references. – dyp Mar 28 '14 at 18:47
  • Nitpick - the generated `operator()` is `const` unless you mark the lambda `mutable`. – Praetorian Mar 28 '14 at 19:40
  • Thanks for calling those out. I purposefully left out some details (including `delete`d constructors), but it's good to point it out so readers are aware. I've edited the answer to include a disclaimer. – Lilshieste Mar 28 '14 at 21:52