Can using lambda in header files violate ODR?

Can the following be written to the header file:

inline void f () { std::function<void ()> func = [] {}; } 

or

 class C { std::function<void ()> func = [] {}; C () {} }; 

I think that in each source file the lambda type may be different, and therefore the result contained in std::function ( target_type will be different) will be different.

Is this an ODR ( one rule of definition ), despite the fact that it looks like a common template and a reasonable thing? Does the second sample violate ODR every time, or only if at least one constructor is in the header file?

+63
c ++ language-lawyer lambda c ++ 11 one-definition-rule
Jan 11 '16 at 9:19
source share
2 answers

This boils down to the fact that the lambda type is different from translation units. If so, this can affect the output of the template argument and potentially call the different functions that should be called - in what should be sequential definitions. This will violate ODR (see below).

However, this is not intended. In fact, this problem has long been affected by the main problem 765 , which specifically specifies built-in functions with external communication - for example, f :

7.1.2 [dcl.fct.spec] paragraph 4 indicates that local static variables and string literals appearing in the body of an inline function with an external link must be the same entity in each translation unit in the program. Nothing is said, however, about whether local types should also be the same.

Although the corresponding program could always determine this using typeid, recent changes in C ++ ( allowing local types to be used as templates for type arguments, closing classes for lambda expressions ) make this question more pressing.

Notes for the July 2009 meeting:

Types must be the same.

The resolution now included the following wording in [dcl.fct.spec] / 4 :

The type defined inside the body of the extern inline function is the same type in each translation unit.

(NB: MSVC does not yet refer to the above wording, although it may be in a future release ).

Thus, Lambdas inside such function bodies is safe, since the definition of the type of closure is really in the block area ( [expr.prim.lambda] / 3 ) <w> Therefore, several definitions of f have always been clearly defined.

This resolution, of course, does not cover all scenarios, since there are many other types of entities with external communication that can use lambdas, in particular, function templates - this should be covered by another main problem.
Meanwhile, Itanium already contains the appropriate rules to ensure that these types of lambda match in more situations, so Clang and GCC should basically behave as intended.




Standard information on why various types of faults are an ODR violation. Consider the bullet points (6.2) and (6.4) in [basic.def.odr] / 6 :

There may be more than one definition [...]. For such an object with the name D defined in several translation units, each definition of D should consist of the same sequence of tokens; and

(6.2) - in each definition of D , the corresponding names scanned according to [basic.lookup], refers to the entity defined in the definition of D or should refer to the same object after ([over.match]) and after negotiating a partial template specialization ( [temp.over]), [...]; and

(6.4) - in each definition of D overloaded operators in question, implicit calls to transformation functions , constructors , new operator functions and operator removal functions must refer to the same function or function defined in the definition of D ; [...]

This effectively means that any functions called in the definition of an entity must be the same in all units of a translation - or have been defined inside its definition , as well as local classes and their members. That is, the use of lambda as such is not problematic, but it is clear that there is a transfer of it to functional templates, since they are defined outside the definition.

In your C example, the type of closure is defined inside the class (whose scope is the least encompassing). If the type of closure differs in two TUs that the standard may inadvertently imply with the uniqueness of the type of closure, the constructor creates an instance and invokes various specializations of the function constructor template, violating (6.4) in the above quote.

+37
Jan 11 '16 at 12:16
source share

UPDATED

In the end, I agree with @Columbo's answer, but I want to add a practical five cents :)

Although violating ODR sounds dangerous, in this particular case this is not a serious problem. Lambda classes created in different specifications are equivalent, except for their types. Therefore, if you do not need to deal with the type specified by the lambda header (or type depending on the lambda), you are safe.

Now that an ODR violation is reported as an error, there is a high probability that it will be fixed in compilers that have a problem, for example. MSVC and perhaps some others that do not follow Itanium ABI. Note that Itanium ABI-compatible compilers (e.g. gcc and clang) already produce the ODR-correct code for the lambdas defined by the header.

+6
Jan 12 '16 at 15:09
source share



All Articles