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>

This paper proposes removal of digraphs.

0.1

R0 September 2026

1

Introduction

2

History

2.1

Timeline

3

Design Space

3.1

Splicers

3.2

Interpolated string literals

4

Affected encodings

5

Usage Analysis

6

Compatibility

7

Alternatives to full digraph removal

7.1

Partially remove digraphs

7.2

Partially reintroduce trigraphs

7.3

Deprecate digraphs first

8

Wording

9

Acknowledgements

10

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 (and, or, 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!):

1984
August 27, 1984
C and ANSI Standard

In a Usenet post, Keld Simonsen suggests digraphs as a solution to the problem of missing characters in some ISO 646 variants.

Proposed digraphs:

(. {
). }
(* [
)* ]
!. |
!* \
1985
March, 1985
ECMA-94 is published

ISO/IEC 8859-1 is published as ECMA-94, providing a solution for the problem of missing characters

1985~1986
ANSI C introduces trigraphs
[BYTE 1986] page 138 (146 in the PDF)

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.

1987
October 15, 1987
Tøndering, Alternative to Trigraphs

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.

December 11, 1987
X3J11 Meeting No. 19

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
1988
June 13, 1988
Simonsen and Stroustrup, A European Representation for ISO C (Revised N023)

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 argv!a to spell argv[a]. Also note that the paper argues that it should not be legal to mix these digraph constructs with the tokens they replace.

August 19, 1988
Danish Comments on ISO/DP 9899-C

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.

October 27, 1988
X3J11 Meeting No. 21
[X3J11/88-155] pages 4-6

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 a[] (since x!() is presumably invalid.)

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:

Straw vote: 0 accept digraphs a la 88-134 40 no

Due to this poll outcome digraphs were not adopted for C89.

1990
November 26, 1990
A European Representation for ISO C

Bjarne Stroustrup publishes a paper entitled "A European Representation for ISO C" as WG14/N141. Unfortunately this paper is not archived.

1991
May 13-15, 1991
WG14 Meeting in Tokyo, Japan
[WG14/N173] pages 6-8

Lots of discussion around digraphs, mostly about in which lexing phase they should be dealt with. WG14 endorses the digraph proposal.

*** Resolution #4: WG14 endorses the digraph proposal from Denmark (E-mail WG14.109 page 3) that involves the addition of five alternate spellings <%, %>, <:, :>, and %% for {, }, [,], and #, respectively, and the new header <iso646.h>. [Implicit in this was that these new spellings be recognized in translation phase 5 NOT phase 1.]
June 17-21, 1991
X3J16 Meeting No. 6
[WG21/N0013] pages 7 and 14

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.

More on this later.

The following digraphs are proposed:

<% {
%> }
<: [
:> ]
%% #

Two polls were taken:

1. Add these names and digraphs to C++ as alternate spellings for their corresponding tokens. Motion passed: 24 yes, 2 no. 2. Ask WG14 to consider replacing not_eq with <>. Motion passed: 22 yes, 2 no.
1992
May 13-15, 1992
WG14 Meeting in Salt Lake City, USA

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:

Straw vote: 2 accept N218 with the two changes 15 no Straw vote: 8 accept N222 with <> replaced by NE 9 no Straw vote: 7 try to solve digraph problem some way 12 no

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.

November 2-6, 1992
X3J16/WG21 Meeting

Problems with digraphs in keywords are encountered. To quote the minutes:

Roskind objected to the proposal because allowing space between new and [] conflicts with existing practice. Gibbons explained that there are digraphs for [ and ] (<: and :> respectively) and the subgroup did not want keywords containing digraphs.

November 13, 1992
Danish Contribution to Normative Addendum

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.

December 9, 1992
Revised Danish Contribution to Normative Addendum

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.

1993
December 1-3, 1993
WG14/X3J11 Meeting in Hawaii

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.

[AGENDA 3.1] Result of PDAM Balloting (PDAM == Proposed Draft Amendment) Probable voting of WG14 members: [...] Netherlands: No with comments. Digraphs are problem. UK: No with comments. Digraphs are problem.
2009
March 3, 2009
Deprecating trigraphs

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.

June 19, 2009
Comment on Proposed Trigraph Deprecation

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:

  1. 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.
  2. There is no replacement for trigraphs in quotes
  3. Compiling the same code between ASCII and EBCDIC without #ifdefs
  4. 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.

September 24, 2009
Replacing Trigraphs

Daveed Vandevoorde publishes a paper exploring an alternative solution to [UK 11].

2010
March 2010
Global-scope template arguments vs the <: digraph

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 <::.

2014
May 6, 2014
Removing trigraphs??!

Richard Smith publishes a paper proposing removal of trigraphs.

October 10, 2014
IBM comment on preparing for a Trigraph-adverse future in C++17

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.

November 3-8, 2014
WG21 Meeting No. 59

Removal of trigraphs is polled in WG21 plenary:

CWG Motion 18 straw poll results were: In favor: 57 Opposed: 2 Abstain: 10 Sutter declared consensus, motion approved.

🎉 Trigraphs are removed from C++.

2022
March 2, 2022
Removing trigraphs??!

Robert C. Seacord publishes a paper proposing removal of trigraphs from C to align with C++.

July 18-22, 2022
WG14 Meeting

The C committee discusses removal of trigraphs.

A poll is taken:

^Straw poll: Does WG14 want N2940 (removing trigraphs) into C23? 10/6/2. (adopted) N2940 (removing trigraphs) goes into C23. Svoboda: Can we go 2/3rds for removing a feature? Keaton: We go by consensus. We have consensus here.

🎉 Trigraphs are removed from C.

2023
January 23-28, 2023
WG14 Meeting

WG14 discusses trigraph unremoval following ballot comment CA-006 and other trigraph-related comments.

Straw poll: (opinion) Does WG14 want to include trigraphs in C23, in any form? Result: 7-16-3 (no consensus) Straw poll: (decision) Should WG14 REJECT comments CA-006, US15-048, NEN/NL1-049, US18-050, US17-051, US16-052? Result: 20-4-3 (rejected)

3. Design Space

3.1. Splicers

Splicers from [P2996] were accepted for C++26 with the proposed syntax [: expr :]. However, we are not allowed to use digraphs to spell this as <:: expr ::>.

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:

t"foo { bar %> baz"

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:

t"foo { bar :>5}"

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, "{:>5}" 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:

The following 9 codecs require digraphs and are otherwise fully usable:

We can narrow that list down even further. The most interesting digraph is %:, since it can be used to spell #pragma. Some compilers already have pragmas to specify the source encoding - for example, the IBM z/OS XL compilers support #pragma filetag (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:

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 %:include 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:

  1. Compiler test code and other lexer test code
  2. Demo code, examples and exercises
  3. 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. %:if yields 22 results, all of which are university assignments, inconsistent or false positives.

For the promising %:pragma we get 107 results, most if not all of which are inconsistent. For %:pragma filetag 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 templ<::arg> (without space after <).

6. Compatibility

At the time of writing almost all major compilers still support trigraphs as an extension. For instance:

Some compilers already support disabling digraph support altogether. For instance:

7. Alternatives to full digraph removal

As mentioned before, compilers that have pragmas such as #pragma filetag 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 _Pragma("...") 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 #pragma as %:pragma.

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 %:pragma filetag 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 #pragma filetag 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 #pragma filetag as ??=pragma filetag.

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 indicated

5 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 d-char, r-char, or delimiting parenthesis is identified. The raw string literal is defined as the shortest sequence of characters that matches the raw-string pattern

encoding-prefixopt R raw-string

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:

preprocessing-op-or-punc:
preprocessing-operator
operator-or-punctuator
preprocessing-operator:
one of
# ## %: %:%:
operator-or-punctuator:
one of
{ } [ ] ( ) [: :]
<% %> <: :> ; : ...
? :: . .* -> ->* ^^ ~
! + - * / % ^ & |
= += -= *= /= %= ^= &= |=
== != < > <= >= <=> && ||
<< >> <<= >>= ++ -- ,
and or xor not bitand bitor compl
and_eq or_eq xor_eq not_eq

Each operator-or-punctuator is converted to a single token in translation phase 6 ([lex.phases]).

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 Alternative Primary
<% { and && and_eq &=
%> } bitor | or_eq |=
<: [ or || xor_eq ^=
:> ] xor ^ not !
%: # compl ~ not_eq !=
%:%: ## bitand &

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:

#define LPAREN() ( #define G(Q) 42 #define F(R, X, ...) __VA_OPT__(G R X) ) int x = F(LPAREN(), 0, <:[-); // replaced by int x = 42;

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 <%, %>, <:, :>, %: and %:%: are no longer usable as alternative representations of other 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 %:%: 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.


10. References

[WG14/N052] Simonsen and Stroustrup, A European Representation for ISO C (Revised N023) https://www.open-std.org/jtc1/sc22/wg14/www/docs/n052.pdf
[WG14/N224] Minutes of meeting of WG14 - C held in Salt Lake City USA, 1992-05-13/15 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n224.pdf
[CWG1104] Global-scope template arguments vs the <: digraph https://cplusplus.github.io/CWG/issues/1104.html
[WG14/N3044] Draft Minutes of 2022 July 18-22 WG14 Meeting https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3044.txt
[WG21/N4210] IBM comment on preparing for a Trigraph-adverse future in C++17 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4210.pdf