为什么在 constexpr 上下文中使用保留的标识符名称未诊断?

use*_*113 3 c++ undefined-behavior language-lawyer

基于以下两条规则:

  • 使用以“_”+大写字母开头或包含双下划线的标识符是未定义的行为。
  • constexpr 表达式中不允许有未定义的行为 -> 编译器不应编译。

那么为什么编译器不抱怨这个呢?

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 吗?

dfr*_*fri 7

constexpr 表达式中不允许未定义的行为-> 编译器不应编译

它比这个更窄一些;根据[expr.const]/5/5.7特别是:

表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:

  • [...]
  • /5.7 具有[intro] 到 [cpp] 中指定的未定义行为的操作;

现在,[简介] 到 [cpp] 包括:

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]
Run Code Online (Sandbox Code Playgroud)

然而,关于全局名称下划线的规则来自 [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。

  • @LanguageLawyer 我同意。有人可能会问,是否应该从 [lex] 和 [cpp] 中完全删除术语“UB”?如果标准引导我们对“UB 类型”进行分类(例如词法分析器与实现行为),那么它应该使用两个不同的类别。我不明白什么禁止仅使用 IFNDR 来处理 [lex] 和 [cpp] 违规(对于那些无法诊断的),而不是 UB;从简化的角度来看,我喜欢将 IFNDR 视为程序的静态属性,而 UB 通常是运行时属性(尽管 odr 违规很难用该定义进行分类)。 (2认同)