当前位置:  开发笔记 > 编程语言 > 正文

可以在头文件中使用lambda违反ODR吗?

如何解决《可以在头文件中使用lambda违反ODR吗?》经验,为你挑选了2个好方法。

可以在头文件中写入以下内容:

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

要么

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

我想在每个源文件中,lambda的类型可能不同,因此包含的类型std::function(target_type的结果会有所不同).

这是一个ODR(一个定义规则)违规,尽管看起来像一个共同的模式和合理的事情?第二个示例是每次都违反ODR还是仅在头文件中至少有一个构造函数?



1> Columbo..:

这归结为lambda的类型是否因翻译单位而异.如果是这样,它可能会影响模板参数推断,并可能导致调用不同的函数 - 这意味着一致的定义.这将违反ODR(见下文).

但是,这不是预期的.实际上,这个问题已经被核心问题765触及了,它特别命名了具有外部链接的内联函数 - 例如f:

7.1.2 [dcl.fct.spec]第4段规定出现在具有外部链接的内联函数体中的局部静态变量和字符串文字必须是程序中每个翻译单元中的相同实体.然而,没有任何关于本地类型是否也需要相同的说法.

虽然一致的程序总是可以通过使用typeid来确定这一点,但最近对C++的更改(允许本地类型作为模板类型参数,lambda表达式闭包类)使这个问题更加紧迫.

2009年7月会议记录:

类型旨在相同.

现在,该决议将以下措辞纳入[dcl.fct.spec]/4:

extern inline函数体内定义的类型在每个翻译单元中都是相同的类型.

(注意:MSVC尚未考虑上述措辞,尽管可能在下一版本中).

因此,这些函数体内的Lambdas是安全的,因为闭包类型的定义确实在块范围内([expr.prim.lambda]/3).
因此,多种定义f都是明确定义的.

这个解决方案当然不包括所有场景,因为有更多种类的具有外部链接的实体可以使用lambda,特别是功能模板 - 这应该被另一个核心问题所涵盖.
与此同时,Itanium已经包含适当的规则来确保这些lambda类型在更多情况下重合,因此Clang和GCC应该已经大部分按预期运行.


关于为什么不同的闭包类型是ODR违规的标准如下.考虑[basic.def.odr]/6中的要点(6.2)和(6.4):

[...]可以有多个定义.鉴于这样一个名称D在多个翻译单元中定义的实体,则每个定义D应由相同的令牌序列组成; 和

(6.2) - 在[basic.lookup]中查找的相应名称的每个定义中D,应指过定义中定义的D实体,或者在重载解析后引用同一实体([over.match]) )和部分模板专业化([temp.over])匹配后,[...]; 和

(6.4) - 在每个定义中D,所引用的重载运算符,转换函数,构造函数,运算符新函数和运算符删除函数的隐式调用,应引用相同的函数,或者定义中定义的函数 D ; [...]

这实际上意味着实体定义中调用的任何函数在所有翻译单元中应该是相同的 - 或者已经在其定义中定义,如本地类及其成员.即使用lambda本身并不成问题,但将其传递给函数模板显然是因为这些是在定义之外定义的.

在您的示例中C,闭包类型在类中定义(其范围是最小的封闭类).如果闭包类型在两个TU中不同,标准可能无意中暗示了闭包类型的唯一性,则构造函数实例化并调用function构造函数模板的不同特化,违反上述引用中的(6.4).


@GeorgeStocker如果它不能被移动,你应该保持原样.请不要删除有价值的讨论; 这是一个.
@GeorgeStocker如果这么简单,为什么没有一个自动化的过程,当列表变得太长时,盲目删除所有注释,就像你那样做?我们都是程序员,我们都知道实现起来有多么微不足道.在这种情况下,为什么网站需要主持人的人工干预?也许是因为主持人在做这件事时应该*使用他们的判断*,正是为了避免丢失有价值的内容?关于"把它放在你的答案中":你是否让Columbo有机会在删除之前将内容移到答案中?
@GeorgeStocker评论在哪里?
我非常生气,因为有些人刚刚参加了这次讨论,所以评论被删除了.谁能在世界上做一个像这样的火腿呢?

2> oliora..:

更新

毕竟我同意@Columbo的答案,但想加实用五分钱:)

虽然ODR违规听起来很危险,但在这种特殊情况下并不是一个严重的问题.在不同的TU中创建的lambda类除了它们的typeid之外是等价的.因此,除非你必须处理头定义的lambda(或一个取决于lambda的类型)的typeid,否则你是安全的.

现在,当ODR违规被报告为错误时,很有可能在具有问题的编译器中修复它,例如MSVC以及可能不遵循Itanium ABI的其他一些问题.请注意,符合Itanium ABI标准的编译器(例如gcc和clang)已经为头定义的lambda生成了ODR校正代码.

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有