C++中依赖实现自由度的条件UB是否属于无条件未定义行为?
问题背景
在「C20中使用std::bit_cast创建闭包(lambda)对象是否合法」的相关讨论中,有观点提出:程序是否触发未定义行为(UB),可能取决于实现方如何使用C标准赋予的实现自由度。
以[expr.prim.lambda.closure]/2条款为例:
闭包类型声明于包含对应lambda表达式的最小块作用域、类作用域或命名空间作用域中。[...] 闭包类型不属于聚合类型。实现可采用与下文描述不同的方式定义闭包类型,前提是除了修改以下内容外,不会改变程序的可观测行为:
- (2.1) 闭包类型的大小和/或对齐要求
- (2.2) 闭包类型是否可平凡复制
- (2.3) 闭包类型是否为标准布局类。[...]
该讨论的评论区同时明确,上述场景不属于实现定义行为的范畴:
「实现定义」有非常明确的含义(参考
[intro.abstract]/2条款),当前场景不符合实现定义行为的判定标准。
核心问题
基于上述背景衍生三个核心问题:
- 若程序的UB是否触发取决于上述实现自由度的选择,该程序是否属于存在无条件UB?
- 该场景是否可参考
[intro.abstract]/5条款判定? - 这类程序在C++标准术语中该如何描述?
专业解答
是否属于无条件UB
是的,这类程序属于无条件UB,和具体实现最终选择了哪一种自由度参数无关。[expr.prim.lambda.closure]/2给出的实现自由度,本质是标准允许实现对闭包类型的内部细节做差异化调整,该权限的核心前提是「不会改变符合标准的良构程序的可观测行为」。如果某个程序的正确性需要依赖该条款下的实现选择,说明该程序本身就突破了标准对良构程序的要求,本身就不具备可移植性和合法性。
是否可参考[intro.abstract]/5条款判定
完全可以。[intro.abstract]/5条款明确规定:如果程序违反了标准的可诊断规则,或者违反了标准对良构程序的要求且没有明确规定对应的行为,那么程序属于「病态无需诊断(ill-formed, no diagnostic required, IFNDR)」范畴。
你描述的场景完全符合该判定逻辑:标准没有要求实现在该自由度下必须保持某一种固定特征来保障程序正确性,因此程序本质上违反了良构程序的要求,不需要考虑具体实现的选择即可判定其不合法。
对应的标准术语
这类程序的标准官方描述是 ill-formed, no diagnostic required(IFNDR),中文通常译为「病态无需诊断」。
它和普通UB、实现定义行为的核心区别如下:
- 和普通UB的区别:标准不需要编译器对这类错误给出任何诊断提示,开发者很难在编译期发现问题
- 和实现定义行为的区别:实现没有义务固定某一种行为并文档化,完全可以在不同编译版本、不同编译参数下调整该类实现细节,导致程序行为发生变化,这种变化完全符合标准要求,责任完全由程序本身承担。
内容的提问来源于stack exchange,提问作者dfrib

