C++26 tightened unevaluated strings, and GCC did half of it
Some string literals never become objects. The compiler reads them and never encodes them into anything at run time. They appear in five places:
static_assert[[deprecated]][[nodiscard]]extern "C"asm
C++26 calls these unevaluated strings. Through P2361R6, it makes two things ill-formed in them:
- an encoding prefix, as in
static_assert(true, u8"message") - a numeric escape, as in
static_assert(true, "bell \x07 here")
Neither ever meant anything. There is no encoding to apply and no byte to escape into. Tightening this is the sort of cleanup that costs nobody anything.
The two compilers, though, disagree about how much of it is in force.
The same file, two answers
static_assert(sizeof(int) >= 2, "needs \x07 at least two bytes");
That is ill-formed under P2361R6. GCC 16.1 compiles it and runs the program.
Clang 22.1, same source, same flag:
error: invalid escape sequence '\x07' in an unevaluated string literal
What each compiler enforces
| Rule from P2361R6 | GCC 16.1 | Clang 22.1 |
|---|---|---|
encoding prefix, u8"..." or L"..." |
rejected | rejected |
hex escape, "\x07" |
accepted | rejected |
octal escape, "\007" |
accepted | rejected |
So GCC has the first half and not the second. Both compilers reject the prefix in static_assert and in [[deprecated]] alike. Real code is most likely to hit the prefix case. Only Clang follows through on escapes.
The diagnostics are worth comparing too. Clang names the concept:
an unevaluated string literal cannot have an encoding prefix
GCC says:
a wide string is invalid in this context
That message is true but leaves the reader to work out which context and why. The paper introduced a term precisely so that this error could use it.
The other half of the cleanup
P1854R4, applied as a defect report, deals with the opposite case: literals that are evaluated but contain a character the execution encoding cannot represent. Compilers used to substitute such a character silently. MSVC’s habit of turning it into ? is the best-known example. C++26 makes it ill-formed, so a literal either means what it says or fails to compile.
Nothing about evaluated literals changed otherwise. Every prefix and every escape still works exactly as before.
Whether this matters
Directly, it barely matters. People do not write u8 on a static_assert message on purpose. A \x07 in an assertion message is a typo rather than a technique.
Indirectly, it is a reminder worth having. A paper in the standard, even with a feature-test macro, says nothing about how much of it your compiler implements. This two-clause rule landed in the working paper years ago, and today one major compiler enforces one clause. Compile the thing you are relying on.
Sources: P2361R6 “Unevaluated strings” · P1854R4 · Sandor Dargo’s walkthrough of both papers · compiler behaviour verified on Compiler Explorer, GCC 16.1 and clang 22.1.0.