Undefined behavior is not allowed in constexpr expressions -> compiler should not compile
It's a bit more narrow than this; as per [expr.const]/5, /5.7 in particular:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- [...]
- /5.7 an operation that would have undefined behavior as specified in [intro] through [cpp];
Now, [intro] through [cpp] includes:
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]
The rule about underscore for global names, however, comes from [library], particularly [reserved.names]/2 and [global.names]/1 in [library], which is not covered by "[intro] through [cpp]".
[reserved.names]/2 If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined.
[global.names]/1 Certain sets of names and function signatures are always reserved to
the implementation:
- (1.1) Each name that contains a double underscore __ or begins with an underscore followed by an uppercase letter ([lex.key]) is reserved
to the implementation for any use.
- (1.2) Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
Now, [lex.name]/3 also includes the same rule for reservation of identifiers
In addition, some identifiers are reserved for use by C++ implementations and shall not be used otherwise; no diagnostic is required.
- (3.1) Each identifier that contains a double underscore __ or begins with an underscore followed by an uppercase letter is reserved to the
implementation for any use.
- (3.2) Each identifier that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
A violation of [lex.name]/3 is ill-formed no diagnostic required (IFNDR), which is not the same as undefined behaviour; as per [expr.const]/5.7 above some UB should actually be diagnosed (constexpr contexts).
The limitations of [expr.const]/5.7 to UB as per [intro] through [cpp] is arguably intentionally limiting the rule to avoid constructs that is UB for the typical C++ implementor but not the STL library implementors, e.g. rules in [library]. This could also be a wording defect, particularly as the rules of [lex.name]/3 goes from IFNDR to UB only "after the fact" of [reserved.names]/2 in [library].
Thus, this "kind" of undefined behaviour (UB) does arguably not fall under the UB that disqualifies an expression from being a core constant expression.