use*_*113 3 c++ undefined-behavior language-lawyer
基于以下两条规则:
那么为什么编译器不抱怨这个呢?
constexpr int _UB() {return 1;}
int main() {
constexpr int a = _UB();
return a;
}
Run Code Online (Sandbox Code Playgroud)
另外,我看到很多专业的、符合 MISRA 的代码似乎违反了此命名规则,请参见此处:
#ifndef __STM32F732xx_H
#define __STM32F732xx_H
Run Code Online (Sandbox Code Playgroud)
所有这些库都调用 UB 吗?
constexpr 表达式中不允许未定义的行为-> 编译器不应编译
它比这个更窄一些;根据[expr.const]/5,/5.7特别是:
表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:
- [...]
- /5.7 具有[intro] 到 [cpp] 中指定的未定义行为的操作;
现在,[简介] 到 [cpp] 包括:
Run Code Online (Sandbox Code Playgroud)1 Scope[intro.scope] 2 Normative references[intro.refs] 3 Terms and definitions[intro.defs] 4 General principles[intro] 5 Lexical conventions[lex] 6 Basics[basic] 7 Expressions[expr] 8 Statements[stmt.stmt] 9 Declarations[dcl.dcl] 10 Modules[module] 11 Classes[class] 12 Overloading[over] 13 Templates[temp] 14 Exception handling[except] 15 Preprocessing directives[cpp]
然而,关于全局名称下划线的规则来自 [library],特别是 [library] 中的[reserved.names]/2和[global.names]/1,不包含在“[intro] 到 [cpp] 中” ”。
[reserved.names]/2如果程序在保留名称的上下文中声明或定义名称,除非本子句明确允许,否则其行为是未定义的。
[global.names]/1某些名称和函数签名集始终保留给实现:
- (1.1) 每个包含双下划线 __ 或以下划线后跟大写字母 ([lex.key]) 开头的名称都保留给实现以供任何使用。
- (1.2) 每个以下划线开头的名称都保留给实现,用作全局命名空间中的名称。
现在,[lex.name]/3也包含相同的标识符保留规则
此外,一些标识符保留供 C++ 实现使用,否则不得使用;无需诊断。
- (3.1) 每个包含双下划线 __ 或以下划线后跟大写字母开头的标识符都保留给实现以供任何使用。
- (3.2) 每个以下划线开头的标识符都保留给实现,用作全局名称空间中的名称。
违反 [lex.name]/3 是不正确的,无需诊断 (IFNDR),这与未定义的行为不同;根据上面的 [expr.const]/5.7,实际上应该诊断一些 UB(constexpr 上下文)。
根据 [intro] 到 [cpp],[expr.const]/5.7 对 UB 的限制可以说是有意限制规则,以避免为典型 C++ 实现者而非 STL 库实现者构建 UB,例如 [library 中的规则] ]。这也可能是一个措辞缺陷,特别是因为 [lex.name]/3 的规则仅在 [library] 中的 [reserved.names]/2 “事后”才从 IFNDR 转到 UB。
因此,这种“类型”的未定义行为 (UB) 可以说不属于使表达式失去核心常量表达式资格的 UB。