Removing Digraphs
- Document number:
- P4235R0
- Date:
2026-09-22 - Audience:
- SG16
- SG22
- EWG
- Project:
- ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
- Reply-to:
- Matthias Wippich <mfwippich@gmail.com>
R0 September 2026
Introduction
History
Timeline
Design Space
Splicers
Interpolated string literals
Affected encodings
Usage Analysis
Compatibility
Alternatives to full digraph removal
Partially remove digraphs
Partially reintroduce trigraphs
Deprecate digraphs first
Wording
Acknowledgements
References
Revision history
0.1. R0 September 2026
Original version of the paper.
1. Introduction
Digraphs are a complicated solution to a very old problem, which cause more problems than they solve in a modern environment. Digraphs also severely limit the design space of C++, although as we have seen with [P2996] we are already fine with special-casing our way out of this pickle.
This however leads to a rather unfortunate situation. If you need to use a source encoding that requires use of digraphs, you cannot use all of C++26 directly, resulting in a fragmented and needlessly complex language. Some parts you can use if you require certain digraphs, some you don't.
Since we are most likely going to continue seeing similar problems, this paper proposes to remove the digraphs , , , , and from the language entirely.
This does not affect the alternative keyword tokens (, , etc.).
2. History
The GCC documentation notes:
Apparently in the 1990s some computer systems had trouble inputting these characters, or trouble displaying them. These digraphs almost never appear in C programs nowadays, but we mention them for completeness.
What this refers to is systems that require the source encoding to be something that does not have the characters , , , or . In such cases, the corresponding digraphs are required to write those characters in source code.
Such encodings are rather uncommon nowadays, but they do exist.
Historically digraphs were introduced because of [ISO 646], which includes several national variants that do not have square brackets or curly braces. For example, the [ISO-IR-021] (also known as DIN 66003) variant used in Germany replaces with , with , with and with .
Keld Simonsen pointed out this problem with some ISO 646 variants including the Danish [ISO-646-DK] in a 1984 usenet post. To address this issue, he suggested the following digraphs as a solution ([Simonsen, 1984]):
(.→{).→}(*→[)*→]!.→|!*→\
Even then Martin Minow argued against such a solution, as it was a vanishing problem even at the time.
To quote [Minow, 1984]:
There isn't any really good solution -- it is highly unlikely that the C standardization committee will remove these characters from the language. While most of them can be replaced by suitable #defines, several cannot, notably backslash. The only short-term solution would be for the parties affected to write NRC-specific pre-processors.
In the long term, however, the problem will go away as people move to an 8-bit character set such as Dec-Multinational or the pending ISO standard that is almost identical to it.
That "pending ISO standard" Minow mentions later became [ISO/IEC 8859]. The relevant part of ISO/IEC 8859 for the Danish language is ISO/IEC 8859-1, which was first published in March 1985 as [ECMA-94].
ECMA-94 fully solves the described issue by using the 8th bit in an 8-bit byte to allow for another 96 printable characters, which includes everything that was potentially missing in some ISO 646 variants.
Interestingly, [ISO-IR-027] even lacks , making four of the digraphs we eventually ended up with unusable.
While it is explicitly based on ISO 646, ISO-IR-027 is not a true variant - some of the added letters are mapped to code points which are invariant in ISO 646.
However, this shows that even for its original purpose, digraphs are more of a band-aid than a real solution.
2.1. Timeline
So, how did we get here? Unfortunately not a lot of documents from the early days survived. However, the publicly available X3J11, X3J16, WG14 and WG21 documents allow reconstructing roughly this timeline (please reach out if you have corrections or additional information!):
In a Usenet post, Keld Simonsen suggests digraphs as a solution to the problem of missing characters in some ISO 646 variants.
Proposed digraphs:
(.→{).→}(*→[)*→]!.→|!*→\
ISO/IEC 8859-1 is published as ECMA-94, providing a solution for the problem of missing characters
Trigraphs have been added to the X3J11 ANSI C standard.
Unfortunately no minutes of the actual meeting where it was voted in seem to have survived. However, the March edition of BYTE magazine notes:
To make C easier to use internationally and to promote a wider use of C as an international language, the committee chose to address the problem of the "overused" ASCII character set within the Information Bulletin by defining a group of trigraph character sequences to act as operator equivalents.
A WG14 proposal for digraphs as alternative to trigraphs is published by Tøndering.
WG14's document log lists this document, however the paper itself does not seem to have survived.
X3J11 votes against a motion to solve trigraph readability.
Plauger presented a request by Tøndering (87-223, 87-224, 87-248) that we add digraphs for braces and brackets, and other readability aids to avoid most uses for trigraphs.
Straw vote: 4 need to solve trigraph readability problem lots no
A paper proposing digraphs is published by Simonsen and Stroustrup. The relevant proposed alternative tokens in this paper are:
(:→{:)→}!(→[)→]??/→\
Note that for is not a typo. Here's a code example from the paper:
main ( argc , argv ) char * argv ! ( ) ; ( : if ( argc < 1 cor * argv ! ( 1 ) == ' ??/0 ' ) return ; printf ( " Hello,%s??/n " , argv ! ( 1 ) ) ; : )
The paper also discusses using an infix notation to spell .
Also note that the paper argues that it should not be legal to mix these digraph constructs with the tokens they replace.
The Danish NB clarifies that reason for their "no" vote on DP 9899 is the remaining problem with regards to representation in non-ASCII ISO 646 character sets.
Attached is another early version of the Danish digraph proposal. In this version the following replacements are proposed:
(:→{:)→}a!b→a[b]
This document also notes:
The new constructs have been implemented as an undocumented feature in the C++ compiler by Bjarne Stroustrup, AT&T Bell Laboratories.
Plauger reports that Denmark objected strongly to the failure of X3J11 to adopt any form of digraphs and that he is authorized to submit the final X3J11 draft directly for registration as a DIS only if both the UK and Denmark NBs do not object to its content.
Several arguments against the digraph proposal are raised, notably:
Plum observed that people were at liberty to define various kinds of macros to change the syntactic sugar that sweetens C. He felt that X3J11 should take no stand on a particular set. He also observed that the proposed extensions did not solve the problem of representing punctuation readably within string literals, and do not solve the problem of writing declarations such as
(sincea [ ] is presumably invalid.)x ! ( ) Plum asked that X3J11 respectfully say no to the request from WG14. He also urged the Danish members of WG14 to reconsider their opposition to the draft as it stands.
Gwyn felt that the problem as presented was a red herring. He stated that all European shops that he knew had found various ways to deal with the presentation problem in ISO 646, and that these solutions did not belong in the standard.
Finally, a poll was taken:
Due to this poll outcome digraphs were not adopted for C89.
Bjarne Stroustrup publishes a paper entitled "A European Representation for ISO C" as WG14/N141. Unfortunately this paper is not archived.
Lots of discussion around digraphs, mostly about in which lexing phase they should be dealt with. WG14 endorses the digraph proposal.
At this meeting digraphs are formally adopted into C++.
This is also the first meeting at which concerns about the digraph are raised. To quote the minutes:
Charney expressed concern about using the
digraph in C++ because of the<: operator. Plum said that Stroustrup long ago said that C++ programmers won't mind adding a space before:: now and then.::
The following digraphs are proposed:
<%→{%>→}<:→[:>→]%%→#
Two polls were taken:
Digraphs and the requests from WG21 are discussed again.
Notably, the digraph was pointed out as superfluous and conflicting with another X3J11.1 proposal and may cause problems for other extensions.
Similar issues are raised about , which additionally seemed to conflict with the language C*.
It's also pointed out that WG21 failed to include as a replacement for .
Some noteworthy comments include:
Gwyn objected (yet again) that the entire digraph approach was an attempt to solve a vanishing problem.
Sounds familiar? Martin Minow made the same observation in 1984.
Plauger noted that no really good technical solution is likely to surface after so many years of study. The issue is now much more politics than technology - are we going to help Denmark find a palatable solution and will Denmark accept one without still more changes? If not, then WG14 must give up on consensus in this area. Otherwise, the entire normative addendum will be at risk.
Eventually, Plum suggested that we either drop the Danish proposal entirely or revert to the version already adopted by X3J16.
At the same meeting another revision of the digraph proposal is seen. In this version the following digraphs are proposed:
<:
→
[
:>
→
]
<%
→
#
<%<%
→
##
<>
→
!=
According to the minutes, several people immediately objected to the continued inclusion of . Also, the following comment was made:
Weil observed that :> collides with a popular extension to Microsoft C.
Several polls were taken:
At the same meeting it was once again clarified that the Danish C Panel insisted on reinstating . Several NBs expressed their positions:
Jones the Only said that the UK now favors macros, but no digraphs because they might change existing code.
Jaeschke reported that the US now opposes any digraph solution, because the proposals keep changing.
Noda said that Japan wants to support the Danish proposal from an international standpoint, but doesn't want to delay the normative addendum. He asked whether the proposal can be made optional instead of part of the C Standard. Simonsen replied that Denmark wants digraphs to be mandatory.
Plauger suggested that Denmark make one more try to draft a proposal that can achieve consensus. If they fail to do so by the Dec '92 meeting, however, they must face the serious possibility that the normative addendum will be voted out for balloting without a digraph proposal.
Problems with digraphs in keywords are encountered. To quote the minutes:
Roskind objected to the proposal because allowing space between
andnew conflicts with existing practice. Gibbons explained that there are digraphs for[ ] and[ (] and<: respectively) and the subgroup did not want keywords containing digraphs.:>
A paper by Keld Simonsen titled "Danish Contribution to Normative Addendum" is published as WG14/N240. Unfortunately the paper itself does not seem to have survived.
A paper by Cordsen titled "Revised Danish Contribution to Normative Addendum" is published as WG14/N247. Unfortunately the paper itself does not seem to have survived.
It is likely that N247 contained the version that eventually got voted into the C standard. Unfortunately no meeting notes around that time remain either, however around this time the mentions of the Danish proposals stops.
Representatives of the UK and Netherlands NBs make it known that they are not happy with digraphs and intend to vote "no" on the proposed draft amendment.
A deprecation is attempted via CWG issue 789 in response to CD comment [UK 11].
CWG had consensus in favor of deprecation in March 2009, but decided not to do that in March 2010. To quote the issue:
The CWG decided not to deprecate trigraphs, acknowledging that there are communities in which they are viewed as necessary. Instead, it was decided to address what was considered to be the most pressing issue regarding trigraphs, that is, recognizing trigraph sequences inside raw string literals.
The Canadian NB clarifies their opposition to deprecation of trigraphs. To quote the paper:
In summary, we highlight the major problems when trigraphs are deprecated:
- Many real users (and not just IBM) do use trigraphs and resent changing their code for the convenience of another group, or implementers, unless there are really good reasons.
- There is no replacement for trigraphs in quotes
- Compiling the same code between ASCII and EBCDIC without #ifdefs
- Interoperate headers between C with trigraphs and C++ without trigraphs
There are additional specific technical problems for EBCDIC which is one of the main constituent, but we will not list them here as we believe the above report serve as powerful reasons to not deprecate trigraphs.
Importantly, N2910 also points out that digraphs are not a complete replacement for trigraphs.
Daveed Vandevoorde publishes a paper exploring an alternative solution to [UK 11].
Bjarne's prediction did not hold - people did end up minding having to add a space before now and then.
To address this, CWG decided to add special casing for .
Richard Smith publishes a paper proposing removal of trigraphs.
IBM publishes a paper discussing their position on trigraph removal.
It is mentioned that all the technical arguments from their 2009 paper [WG21/N2910] still hold. There are still real customers who use EBCDIC.
However, it is also mentioned that trigraph removal based on N3981 is relatively benign, since compilers may still support trigraphs as a conforming extension.
To quote the paper:
After significant consultations within IBM, it is IBM's position that for the harmony of the greater C++ community, we will not oppose C++17 because of the removal of trigraphs. We recognize that C++ is mostly an ASCII-centric language now. We will continue to oppose trigraph removal, because we feel someone must speak for the minority of users who cannot speak for themselves. This is not just taking a moral high ground, but being practical. We realize the tide is against the EBCDIC world and as such, whether trigraph is removed or not, IBM compiler, EBCDIC, and non-ASCII users must plan to operate in such a world and it is best to start now.
Removal of trigraphs is polled in WG21 plenary:
🎉 Trigraphs are removed from C++.
Robert C. Seacord publishes a paper proposing removal of trigraphs from C to align with C++.
The C committee discusses removal of trigraphs.
A poll is taken:
🎉 Trigraphs are removed from C.
WG14 discusses trigraph unremoval following ballot comment CA-006 and other trigraph-related comments.
3. Design Space
3.1. Splicers
Splicers from [P2996] were accepted for C++26 with the proposed syntax . However,
we are not allowed to use digraphs to spell this as .
While that seems to be in direct contradiction of the guarantees we're given in [lex.digraph] paragraph 2
In all respects of the language, each alternative token behaves the same, respectively, as its primary token, except for its spelling.
it actually isn't. The tokens and are distinct preprocessing tokens rather than being
composed from and (or and respectively). Therefore it doesn't matter whether is a valid
alternative spelling for - the splicer syntax does not contain tokens.
Unfortunately that doesn't exactly help if your source encoding does not have square brackets. In such
cases you cannot use this language feature directly - you'd have to find some workaround (such as
inventing some arbitrary replacement sequence that is expanded to during or after transcoding).
3.2. Interpolated string literals
The design problems stemming from digraphs do not end there. In some of the recent discussions around the proposed string interpolation feature ([P3412], [P3951]) some interesting code was brought up. Consider the following:
In an interpolated string literal, the interpolated expression field is wrapped in curly braces. To parse
an interpolated string literal you must therefore switch between regular string literal parsing and expression parsing as soon
as you see a field introducer ().
However, once you parse the interpolated expression things get a little strange.
is an alternative spelling of . We haven't yet returned back to literal parsing, so this would yield the correct token. So, should we be able to signify the end of the interpolation field with ?
Since allowing anything but literal to terminate a interpolation field seems extremely surprising and will most likely not match user expectations, we are once again looking for a workaround because of digraphs.
To make matters worse, there is another ambiguity. Consider an interpolated literal:
In this example, is the digraph for or is it the start of the format specifiers of that field? In a non-interpolated format literal, would be a valid format specifier.
4. Affected encodings
A common way to support arbitrary source encodings is by normalizing the source code through iconv. Therefore, we are typically limited by what iconv supports. Note that iconv also supports several translation character sets that are irrelevant for our purposes. The codec names in this chapter are the primary/canonical names used by iconv rather than necessarily actual codec names.
To figure out whether a particular encoding needs digraphs, we can check whether iconv fails when trying to convert the characters , , , or to that encoding. If it does, then that encoding requires digraphs.
In a similar fashion, other characters that have meaning in C++ can be checked. For example, if an encoding does not support , several digraphs are not usable either. As mentioned before, one such encoding is LATIN-GREEK-1 ([ISO-IR-027]).
On a somewhat recent Arch Linux system, this gives us the following support matrix: View on Google Sheets
Out of 278 considered encodings, the following 39 codecs require digraphs, but are also missing the \ character:
- EBCDIC-FI-SE
- EBCDIC-FR
- EBCDIC-IT
- EBCDIC-PT
- ISO 646 ES
- GREEK7-OLD
- IBM423
- IBM4517
- ISO 646 IT
- NF_Z_62-010
- NF_Z_62-010_1973
- NS_4551-2
- CSA_Z243.4-1985-1
- CSA_Z243.4-1985-2
- DIN_66003
- DS_2089
- EBCDIC-AT-DE
- EBCDIC-CA-FR
- EBCDIC-IS-FRISS
- ISO 646 ES2
- IBM16804
- IBM274
- IBM281
- IBM290
- IBM420
- IBM4899
- IBM803
- ISO_5427
- JIS_C6229-1984-B
- JUS_I.B1.002
- MSZ_7795.3
- NATS-SEFI
- NC_NC00-10
- NS_4551-1
- ISO 646 PT
- ISO 646 PT2
- SEN_850200_B
- SEN_850200_C
- T.61-8BIT
The following 9 codecs require digraphs and are otherwise fully usable:
- BS_4730
- EBCDIC-ES
- EBCDIC-ES-S
- IBM275
- LATIN-GREEK
- EBCDIC-DK-NO
- EBCDIC-UK
- EBCDIC-US
- IBM880
We can narrow that list down even further. The most interesting digraph is , since it can be used to spell .
Some compilers already have pragmas to specify the source encoding - for example, the IBM z/OS XL compilers support (documentation) for this purpose.
If we only have to retain a digraph for , this narrows our list of affected codecs down to the following 17 codecs:
- EBCDIC-FI-SE
- EBCDIC-FR
- EBCDIC-IT
- EBCDIC-PT
- ISO 646 ES
- GREEK7-OLD
- IBM423
- IBM4517
- ISO 646 IT
- NF_Z_62-010
- NF_Z_62-010_1973
- NS_4551-2
- BS_4730
- EBCDIC-ES
- EBCDIC-ES-S
- IBM275
- LATIN-GREEK
While this isn't a huge list, it proves the point that there are still encodings that require digraphs. However, the question remains whether there is any actual use of any of these in practice.
Fortunately those are all rather obscure encodings by today's standards.
5. Usage Analysis
So, are people still using digraphs? Are they targeting somewhat modern C++ versions?
A GitHub code search can unfortunately not answer this question for us, since a lot of use might be in code that isn't publicly available. However, it helps getting a rough idea of the situation and how people use digraphs.
Surprisingly, we can actually find quite a lot of code that uses digraphs. For example is included in 850 files!
However, if you take a closer look at those results, you'll quickly notice that aside from a bunch of false positives most of them fall into one of three categories:
- Compiler test code and other lexer test code
- Demo code, examples and exercises
- Inconsistent use of digraphs and the tokens they replace, possibly for obfuscation purposes
Note how this list is missing "actual production code that uses digraphs for anything other than tests".
For the other digraphs the situation is similar. yields 216 results, most of which are false positives and/or lexer code. yields 22 results, all of which are university assignments, inconsistent or false positives.
For the promising we get 107 results, most if not all of which are inconsistent. For we get 0 results (even for the trigraph spelling we at least get 106 results).
For the digraphs , , and a code search yields a lot more results, but they seem to be almost exclusively false positives - usually because they appear in string literals or comments. Furthermore, yields especially bad results due to qualified template arguments being spelled as (without space after <).
6. Compatibility
At the time of writing almost all major compilers still support trigraphs as an extension. For instance:
- GCC has
- trigraphs - Clang has
- ftrigraphs - MSVC has
/ Zc : trigraphs - EDG has
-- trigraphs
Some compilers already support disabling digraph support altogether. For instance:
- Clang has
- fno - digraphs - EDG has
(which also disables the alternative operators-- no_alternative_tokens ,and etc.)or - MSVC's documentation of compiler warning C4628 and C4629 suggests using digraphs is not supported with
and will cause a warning with/ Ze , however Compiler Explorer does not verify that/ Za
7. Alternatives to full digraph removal
As mentioned before, compilers that have pragmas such as only really need some way of spelling .
In [WG14/N3116] Rajan Bhakta confirms this:
Theoretically the
is the only one we need, to engage the# .pragma
While we could retain or reintroduce some standard way of spelling this, there is another alternative. MSVC, GCC and Clang all explicitly support the spelling. With this spelling no use of is required whatsoever.
Either way, this gives us some additional options that need to be considered.
7.1. Partially remove digraphs
Instead of removing all digraphs, we could remove all digraphs except for (and possibly ). This would allow users to spell as .
This option seems feasible since is relatively harmless - it does not conflict with any other language features in the way or do and we could still get rid of those.
However, it's not clear whether a tiny amount of actual usage of this feature warrants keeping it around for another couple of decades, especially given that there are alternatives.
Furthermore, the usage analysis in §5. Usage Analysis shows that being able to spell is largely a theoretical concern as compilers still have opt-in support for trigraphs.
7.2. Partially reintroduce trigraphs
Since most if not all code that actually uses with an encoding that cannot represent uses the trigraph spelling rather than the digraph spelling, we could also reintroduce just the trigraph. This would allow users to once again spell as .
As with the other options, this mostly is up to whether the C and C++ committees feel like supporting such encodings is worth the effort.
It also seems rather unlikely that either language is going to reuse for any other purposes anytime soon, meaning that this is a conforming extension either way. If compilers want to keep supporting this, they absolutely can.
7.3. Deprecate digraphs first
It seems highly unlikely that deprecation is actually meaningful in this case.
Users that still rely on encodings that require digraphs are likely not going to be able to update their code regardless of deprecation warnings or not. This would just delay removal and is not pursued at this point.
8. Wording
Make the following changes to the C++ Working Draft. All wording is relative to [N5054], the latest draft at the time of writing.
Lexical conventions [lex]
Preprocessing tokens [lex.pptoken]
Modify paragraph 5 as indicated5 If the input stream has been parsed into preprocessing tokens up to a given character:
5.1
If the next character begins a sequence of characters that could be the prefix and initial double quote of a raw string literal, such as `R"`, the next preprocessing token shall be a raw string literal.
Between the initial and final double quote characters of the raw string, any transformations performed in phase 2 (line splicing) are reverted; this reversion is applied before any
5.2
Otherwise, if the next three characters are and the subsequent character is neither nor , the is treated as a preprocessing token by itself and not as the first character of the alternative token .
5.3
Otherwise, if the next three characters are and the subsequent character is not , or if the next three characters are the , is treated as a preprocessing token by itself and not as the first character of the preprocessing token .
[Note:
The tokens and cannot be composed from digraphs.
— end note]
5.4 Otherwise, the next preprocessing token is the longest sequence of [...]
Operators and punctuators [lex.operators]
Modify as indicated.
1 The lexical representation of C++ programs includes a number of preprocessing tokens that are used in the syntax of the preprocessor or are converted into tokens for operators and punctuators:
| | | |
| | | | | | | | |
| | | | | | | ||
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | ||
| | | | | | | ||
| | | |
Each
Alternative tokens [lex.digraphalt]
Rename to [lex.alt] and update all references accordingly.
1 Alternative token representations are provided for some operators and punctuators.
2 In all respects of the language, each alternative token behaves the same, respectively, as its primary token, except for its spelling.
[Note:
The “stringized” values ([cpp.stringize]) of and are different, maintaining the source spelling.
— end note]
The set of alternative tokens is defined in Table 3.
Modify Table 3
| Alternative | Primary | Alternative | Primary | ||
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | |
Remove footnote 10
🞰) These include “digraphs” and additional reserved words. The term “digraph” (token consisting of two characters) is not perfectly descriptive, since one of the alternative preprocessing-tokens is %:%: and of course several primary tokens contain two characters. Nonetheless, those alternative tokens that aren't lexical keywords are colloquially known as “digraphs”.
Preprocessing directives [cpp]
Argument substitution [cpp.subst]
Modify Example 1 as indicated.
[Example:
— end example]
Annex C (informative) [diff]
C++ and ISO C++ 2026 [diff.cpp26]
[lex] lexical conventions [diff.cpp26.lex]
Add new entry
Affected subclauses: [lex.pptoken], [lex.operators] and [lex.alt] Change: The "digraph" tokens Rationale: Resolves fragmentation of the language, opens up design space and simplifies the language. Effect on original feature:
Valid C++ 2026 code that uses the digraphs , , , , and are no longer usable as alternative representations of other tokens., , , , and may become ill-formed or have different meaning in this version of C++. The character sequences , , , , and are no longer recognized as single preprocessing tokens. Occurrences within comments and literals are unaffected.
The meaning of these token sequences may change in future versions of C++.
9. Acknowledgements
Thanks to Corentin Jabot and Hubert Tong for several rather illuminating discussions on the topic.
Thanks to Jan Schultke for the markup language and document generator used for this paper and thanks to all the awesome people who gave feedback on this proposal.