CWG Issue 2300 (original) (raw)
This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 117a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2025-04-13
2300. Lambdas in multiple definitions
Section: 6.3 [basic.def.odr]Status: CD5Submitter: Robert HaberlachDate: 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 void f() {std::function<void()> f = []{};}
appears in two translation units, different specializations offunction'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
- 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, orshall 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 ofD, corresponding _lambda-expression_s 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 appliesrecursively)[_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 ofD (including the closure types of _lambda-expression_s, but excluding entities defined within default arguments or defualt template arguments of either D or an entity not defined withinD). 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-expression_s (7.5.6 [expr.prim.lambda]) appearing in the type of D may result in the different declarations having distinct types, and _lambda-expression_s 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 thebehavior 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 ofg 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 Xcan sppear in multiple translation units of a valid program; the_lambda-expression_s defined within the default argumeht ofX::h within the definition of X denote the same closure type in each translation unit. —_end example_]