Title
Lambdas in multiple definitions
Status
cd5
Section
6.3 [basic.def.odr]
Submitter
Robert Haberlach

Created on 2016-04-11.00:00:00 last changed 7 months ago

Messages

Date: 2021-02-24.00:00:00

Proposed resolution, April, 2019:

Change 6.3 [basic.def.odr] paragraph 12 as follows:

...Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens, for which the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression; and

  • in each definition of D, corresponding names, looked up according to 6.5 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (12.2 [over.match]) and after matching of partial template specialization (13.10.4 [temp.over]), except that a name can refer to

    • a non-volatile const object with internal or no linkage if the object

      • has the same literal type in all definitions of D,

      • is initialized with a constant expression (7.7 [expr.const]),

      • is not odr-used in any definition of D, and

      • has the same value in all definitions of D,

      or

    • a reference with internal or no linkage initialized with a constant expression such that the reference refers to the same entity in all definitions of D;

    and

  • in each definition of D, except within the default arguments and default template arguments of D, corresponding lambda-expressions shall have the same closure type (see below), and

  • in each definition of D, corresponding entities shall have the same language linkage; and

  • in each definition of D, the overloaded operators referred to, the implicit calls to conversion functions, constructors, operator new functions and operator delete functions, shall refer to the same function, or to a function defined within the definition of D; and

  • in each definition of D, a default argument used by an (implicit or explicit) function call or a default template argument used by an (implicit or explicit) template-id or simple-template-id is treated as if its token sequence were present in the definition of D; that is, the default argument or default template argument is subject to the requirements described in this paragraph (and, if the default argument has subexpressions with default arguments, this requirement applies recursively) [Footnote: 9.3.4.7 [dcl.fct.default] describes how default argument names are looked up. —end footnote]; and

  • ...

If D is a template and is defined in more than one translation unit, then the preceding requirements shall apply both to names from the template's enclosing scope used in the template definition (_N4868_.13.8.4 [temp.nondep]), and also to dependent names at the point of instantiation (13.8.3 [temp.dep]). If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. These requirements also apply to corresponding entities defined within each definition of D (including the closure types of lambda-expressions, but excluding entities defined within default arguments or defualt template arguments of either D or an entity not defined within D). For each such entity and for D itself, the behavior is as if there is a single entity with a single definition, including in the application of these requirements to other entities. [Note: The entity is still declared in multiple translation units, and 6.6 [basic.link] still applies to these declarations. In particular, lambda-expressions (7.5.5 [expr.prim.lambda]) appearing in the type of D may result in the different declarations having distinct types, and lambda-expressions appearng in a default argument of D may still denote different types in different translation units. —end note] If the definitions of D do not satisfy these requirements, then the behavior is undefined. program is ill-formed, no diagnostic required. [Example:

  inline void f(bool cond, void (*p)()) {
    if (cond) f(false, []{});
  }
  inline void g(bool cond, void (*p)() = []{}) {
    if (cond) g(false);
  }
  struct X {
    void h(bool cond, void (*p)() = []{}) {
      if (cond) h(false);
    }
  };

If the definition of f appears in multiple translation units, the behavior of the program is as if there is only one definition of f. If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type. The definition of X can sppear in multiple translation units of a valid program; the lambda-expressions defined within the default argumeht of X::h within the definition of X denote the same closure type in each translation unit. —end example]

Date: 2019-07-15.00:00:00

[Accepted as a DR at the July, 2019 meeting.]

A lambda expression in two translation units has distinct closure types, because each such expression's type is unique within the program. This results in an issue with the ODR, which requires that the definitions of an entity are identical. For example, if

  template <int> void f() {std::function<void()> f = []{};}

appears in two translation units, different specializations of function's constructor template are called, which violates 6.3 [basic.def.odr] bullet 6.4.

Issue 765 dealt with a similar problem for inline functions, but the issue still remains for templates.

History
Date User Action Args
2020-12-15 00:00:00adminsetmessages: + msg6350
2020-12-15 00:00:00adminsetstatus: drafting -> cd5
2016-04-11 00:00:00admincreate