This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


2300. Lambdas in multiple definitions

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Robert Haberlach     Date: 2016-04-11

[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.

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

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.6 [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]