| Document #: | Dxxxx |
| Date: | 2026-04-26 |
| Project: | Programming Language C++ |
| Audience: |
EWG |
| Reply-to: |
Matthias Wippich <[email protected]> |
This paper proposes making
$
in identifiers conditionally-supported to align with C23.
Original version of the paper.
One of the most popular extensions to C++ is allowing
$
in identifiers. For example, this is supported by MSVC ([MSVC documentation]), GCC ([GCC documentation]), Clang, EDG (might
need opt-in), icx and nvc++ (see Compiler
Explorer).
In recent years, C has made quite a few changes to what they consider
an identifier. The original rules allowed any implementation-defined
characters, the revised ones did not. To address this, [WG14 N3145] introduced a carve-out to
allow
$
anywhere in identifiers as an implementation extension.
[WG14 N3145] mentions that during
discussion it was noted that allowing
$
in identifiers is a massive syntactic land-grab. However, due to the
popularity of this extension it seems unrealistic that we can ever use
$
for anything that would conflict with $identifiers.
GCC notes in its documentation
However, dollar signs in identifiers are not supported on a few target machines, typically because the target assembler does not allow them. ([GCC documentation])
Examples for such targets are [RS/6000] (because of the AIX assembler), [AVR] and [MMIX].
In theory we could mandate
$
to be valid in identifiers and ask compilers to work around such
problematic assemblers in some way or another. However, the purpose of
this paper is to align the standard with the implementation reality and
C23.
Aside from aligning with C, there are a few more motivating uses.
Some linkers emit linker-defined symbols with dollars in their identifiers.
One such example is armlink from the ARM MDK toolchain:
Unfortunately the examples given in armlink’s documentation are not valid C++.Symbols that contain the character sequence $$, and all other external names containing the sequence $$, are names reserved by ARM. ([armlink documentation])
extern int Image$$ER_ZI$$Limit; // oops, not a valid identifier
extern int Image_ER_ZI_Limit asm("Image$$ER_ZI$$Limit");
However, it seems much cleaner and less error-prone to avoid repetition and instead actually allow such implementations to support dollars in identifiers directly.
Another somewhat common use is in macro identifiers. Github search finds more than 5600 uses in code recognized as C++ ([Github code search])
For example consider#if MSVC
# define $inline_never __declspec(noinline)
# define $inline_always __forceinline
#else
# define $inline_never [[gnu::noinline]]
# define $inline_always [[gnu::always_inline]] inline
#endif
#define $inline(_opt) $inline_##_opt
#define $template template for (auto _ : "")
#define $pi 3
#define $assert(...) \
do { \
((__VA_ARGS__) ? (void)0 : throw std::runtime_error(#__VA_ARGS__)); \
} while (false)
$inline(always) int foo(int a) {
$assert(a > 0);
return a * $pi;
}
$inline(never) int bar(int, char) {
$template {
constexpr auto ctx = std::meta::current_function();
static constexpr auto [... Idx] = std::make_index_sequence<parameters_of(ctx).size()>{};
return (foo([:variable_of(parameters_of(ctx)[Idx]):]) + ... + 0);
}
}
As noted earlier, using $ in any identifiers that survive until the assembler takes over can be problematic with some targets. Since macros are long gone at this point, using dollars in macros is relatively safe.
If used only for macros consistently, this has the benefit of making them visually stand out. Not only does this reduce chances of name collisions, it also helps when surveying code - after all, macros come with sharp edges.
Make the following changes to the C++ Working Draft. All wording is relative to [N5032], the latest draft at the time of writing.
Add a new paragraph after 5.11 [lex.name]/1
[ Note: The character properties XID_Start and XID_Continue are described by UAX #44 of the Unicode Standard. — end note ]
1 The program is ill-formed if an identifier does not conform to Normalization Form C as specified in the Unicode Standard.
[ Note: Identifiers are case-sensitive. — end note ]
[ Note: E [uaxid] compares the requirements of UAX #31 of the Unicode Standard with the C++ rules for identifiers. — end note ]
[ Note: In translation phase 4, identifier also includes those preprocessing-tokens (5.5 [lex.pptoken]) differentiated as keywords (5.12 [lex.key]) in the later translation phase 7 (5.10 [lex.token]). — end note ]
2
The inclusion of
$
in nondigit is conditionally-supported.
[ Note: Use of
$ anywhere in an identifier is
allowed if the implementation supports it. — end note
]
Thanks to Michael Park for the pandoc-based framework used to transform this document’s source from Markdown.