연산자 대체 표현

연산자 대체 표현 (Alternative operator representations)

C++ 소스 코드는 ISO 646:1983 고정 문자 집합(invariant character set)을 포함하는 비(非) ASCII 7비트 문자 집합 어디서든 쓸 수 있어요. 그런데 몇몇 C++ 연산자와 구두점은 ISO 646 코드셋 밖의 문자({, }, [, ], #, \, ^, |, ~)가 필요해요. 이 기호 중 일부(혹은 전부)가 없는 문자 인코딩(예: 독일 DIN 66003)을 쓸 수 있도록, C++는 ISO 646 호환 문자로 이루어진 다음과 같은 대체(token)를 정의해요.

출처: cppreference - Alternative operator representations

본문

대체 토큰 (Alternative tokens)

비(非) ISO646 문자를 쓰는 여러 연산자와 토큰에는 대체 철자가 있어요. 언어의 모든 측면에서 각 대체 토큰은 철자만 다를 뿐 기본 토큰(primary token)과 정확히 똑같이 동작해요. (문자열화 연산자 #가 철자를 드러나게 할 수는 있어요.) 두 글자짜리 대체 토큰은 가끔 digraph라고 불러요. %:%:는 네 글자지만 이것도 digraph로 간주해요.

기본 대체
&& and
&= and_eq
& bitand
| bitor
~ compl
! not
!= not_eq
|| or
|= or_eq
^ xor
^= xor_eq
{ <%
} %>
[ <:
] :>
# %:
## %:%:

트라이그래프 (Trigraphs, C++17에서 제거)

다음 세 문자 그룹(트라이그래프)은 주석과 문자열 리터럴이 인식되기 전에 파싱되고, 트라이그래프가 나타날 때마다 해당 기본 문자로 치환돼요:

기본 트라이그래프
{ ??<
} ??>
[ ??(
] ??)
# ??=
\ ??/
^ ??'
| ??!
~ ??-

트라이그래프는 아주 일찍 처리되기 때문에 주의할 점이 있어요. // Will the next line be executed?????/ 같은 주석은 실제로 다음 줄을 주석 처리하게 되고, "Enter date ??/??/??" 같은 문자열 리터럴은 "Enter date \\??"로 파싱돼요.

참고 (Notes)

문자 &!는 ISO-646 하에서도 고정 문자이지만, 이 문자를 쓰는 토큰에도 더 제약적인 역사적 문자 집합을 수용하기 위해 어쨌든 대체가 제공돼요.

동등성 연산자 ==에는 (eq 같은) 대체 철자가 없어요. 왜냐하면 문자 =는 지원되는 모든 문자 집합에 존재했기 때문이에요.

C와의 호환성 (Compatibility with C)

C 프로그래밍 언어에서는 같은 단어들이 <iso646.h> 헤더 파일에 매크로로 정의돼 있어요. C++에서는 이것들이 언어에 내장되어 있기 때문에 C++ 버전의 <iso646.h><ciso646>는 아무것도 정의하지 않아요. 다만 단어가 아닌 digraph(예: <%)는 핵심 언어의 일부라서 어떤 헤더도 포함하지 않고 쓸 수 있어요. (그렇지 않으면 #가 없는 어떤 문자 집합에서든 쓸 수 없게 되거든요.)

키워드

and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq

예제

다음 예제는 여러 대체 토큰을 사용하는 모습을 보여줘요.

%:include <iostream>

struct X
<%
    compl X() <%%> // destructor
    X() <%%>
    X(const X bitand) = delete; // copy constructor
    // X(X and) = delete; // move constructor

    bool operator not_eq(const X bitand other)
    <%
       return this not_eq bitand other;
    %>
%>;

int main(int argc, char* argv<::>)
<%
    // lambda with reference-capture:
    auto greet = <:bitand:>(const char* name)
    <%
        std::cout << "Hello " << name
                  << " from " << argv<:0:> << '\n';
    %>;

    if (argc > 1 and argv<:1:> not_eq nullptr)
        greet(argv<:1:>);
    else
        greet("Anon");
%>

가능한 출력:

Hello Anon from ./a.out

이 예제를 보면 %:include#include를, <%·%>{·}를, <:·:>[·]를, bitand&를, not_eq!=를, and&&를, compl~를 각각 대체한 것임을 알 수 있어요. 일반적인 코드에서 이렇게 쓸 일은 드물지만, 문자가 부족한 환경에서의 호환성을 위해 존재해요.

표준 문서 (References)

  • C++23 표준 (ISO/IEC 14882:2024) — 5.5 Alternative tokens [lex.digraph]
  • C++20 표준 (ISO/IEC 14882:2020) — 5.5 Alternative tokens [lex.digraph]
  • C++17 표준 (ISO/IEC 14882:2017) — 5.5 Alternative tokens [lex.digraph]
  • C++14 표준 (ISO/IEC 14882:2014) — 2.4 Trigraph sequences [lex.trigraph], 2.6 Alternative tokens [lex.digraph]
  • C++11 표준 (ISO/IEC 14882:2011) — 2.4 Trigraph sequences [lex.trigraph], 2.6 Alternative tokens [lex.digraph]
  • C++03 표준 (ISO/IEC 14882:2003) — 2.3 Trigraph sequences [lex.trigraph], 2.5 Alternative tokens [lex.digraph]
  • C++98 표준 (ISO/IEC 14882:1998) — 2.3 Trigraph sequences [lex.trigraph], 2.5 Alternative tokens [lex.digraph]

더 알아보기 (Learn more)

  • 연산자 우선순위와 결합성 — 다양한 연산자가 어떤 우선순위로 평가되는지 정리한 페이지예요.
  • C 문서 — Alternative operators and tokens.