这归结为lambda的类型在翻译单元之间是否不同。如果确实如此,则可能会影响模板自变量的推导,并有可能导致调用不同的函数,即所谓的一致定义。这将违反ODR(请参阅下文)。
但是,这不是故意的。实际上,核心问题765早已解决了此问题,该问题特别用外部链接命名内联函数,例如f
:
7.1.2 [dcl.fct.spec]段落4指定出现在具有外部链接的内联函数的主体中的局部静态变量和字符串文字在程序中的每个翻译单元中必须是相同的实体。但是,关于本地类型是否同样必须相同的问题,也只字未提。
尽管符合标准的程序始终可以通过使用typeid来确定这一点,但是对C ++的最新更改(允许将本地类型用作模板类型参数,lambda表达式闭包类)使此问题更加紧迫。
2009年7月的会议记录:
类型应相同。
现在,该决议在[dcl.fct.spec] / 4中加入了以下措词:
在extern inline
函数主体中定义的类型在每个翻译单元中都是相同的类型。
(注意:MSVC尚未考虑上述措辞,尽管可能会在下一版本中使用)。
因此,此类函数体内的Lambda是安全的,因为闭包类型的定义确实在块范围内([expr.prim.lambda] / 3)。
因此,对的多个定义f
是明确定义的。
该解决方案当然不能涵盖所有情况,因为有更多种类的具有外部链接的实体可以使用lambda,特别是功能模板-这应该由另一个核心问题解决。
同时,Itanium已包含适当的规则,以确保此类lambda的类型在更多情况下重合,因此Clang和GCC应该已经基本按预期运行。
下面是为什么不同的封闭类型违反ODR的标准规范。考虑[basic.def.odr] / 6中的要点(6.2)和(6.4):
[…]的定义不止一个。给定这样一个D
由多个翻译单元定义的实体,则的每个定义D
都应包含相同的令牌序列;和
(6.2)-在[决议]的每个定义中D
,根据[basic.lookup]查找的对应名称,D
应指代在重载解析([over.match] )以及部分模板专门化([temp.over])匹配之后,[…]; 和
(6.4)-在的每个定义中D
,所引用的重载运算符,对转换函数,构造函数,运算符新函数和运算符Delete函数的隐式调用,均应引用相同的函数,或指向
D
;[…]
这实际上意味着在实体的定义中调用的所有函数在所有翻译单元中都应相同-或已在其定义内定义,例如本地类及其成员。即,lambda本身的使用没有问题,但是将其明确传递给功能模板是有问题的,因为它们是在定义之外定义的。
在带有的示例中C
,闭包类型在类中定义(其范围是最小的闭包类型)。如果闭包类型在两个TU中有所不同(标准可能无意中暗示闭包类型的唯一性),则构造函数实例化并调用function
的构造函数模板的不同专长,这违反了上面引用中的(6.4)。