비교 연산자

비교 연산자 (Comparison operators)

비교 연산자는 인자를 서로 비교해요. 같은지, 다른지, 큰지 작은지 — 여기에 C++20의 3방향 비교(<=>)까지 묶어서 다루는 주제예요.

출처: cppreference - Comparison operators

본문

연산자 이름 문법 오버로드 가능 클래스 안 프로토타입 예 (T) 클래스 밖 프로토타입 예 (T)
같음 (Equal to) a == b bool T::operator==(const U& b) const; bool operator==(const T& a, const U& b);
다름 (Not equal to) a != b bool T::operator!=(const U& b) const; bool operator!=(const T& a, const U& b);
작음 (Less than) a < b bool T::operator<(const U& b) const; bool operator<(const T& a, const U& b);
큼 (Greater than) a > b bool T::operator>(const U& b) const; bool operator>(const T& a, const U& b);
작거나 같음 a <= b bool T::operator<=(const U& b) const; bool operator<=(const T& a, const U& b);
크거나 같음 a >= b bool T::operator>=(const U& b) const; bool operator>=(const T& a, const U& b);
3방향 비교 (C++20) a <=> b R T::operator<=>(const U& b) const;¹ R operator<=>(const T& a, const U& b);¹

¹Roperator<=>의 반환 타입이에요 (아래 참고).

참고:

  • 내장 연산자가 bool을 돌려주는 곳에서 대부분의 사용자 정의 오버로드도 내장처럼 쓰일 수 있도록 bool을 돌려줘요. 다만 사용자 정의 오버로드에서는 반환 타입으로 어떤 타입이든(void 포함) 쓸 수 있어요.
  • UT를 포함해 어떤 타입이든 될 수 있어요.

양방향 비교 (Two-way comparison)

양방향 비교 연산자 표현식은 다음 형태를 가져요.

관계 연산자 (Relational operators):

형태 설명
lhs < rhs (1) lhsrhs보다 작으면 true, 그 외에는 false
lhs > rhs (2) lhsrhs보다 크면 true, 그 외에는 false
lhs <= rhs (3) lhsrhs보다 작거나 같으면 true, 그 외에는 false
lhs >= rhs (4) lhsrhs보다 크거나 같으면 true, 그 외에는 false

동등 연산자 (Equality operators):

형태 설명
lhs == rhs (5) lhsrhs와 같으면 true, 그 외에는 false
lhs != rhs (6) lhsrhs와 같지 않으면 true, 그 외에는 false

내장 양방향 비교 연산자

내장 양방향 비교 연산자에서 lhsrhs에 lvalue-to-rvalue 변환, 배열-포인터 변환(until C++26), 함수-포인터 변환이 적용돼요.

이 변환들을 적용하기 전에 lhsrhs가 둘 다 배열 타입이면 그 비교는 폐기 예정이에요. (C++20 이후, until C++26)

내장 관계 연산자에서 피연산자 중 하나가 포인터면 다른 피연산자에 배열-포인터 변환이 수행돼요.

내장 동등 연산자에서 피연산자 중 하나가 포인터나 널 포인터 상수면 다른 피연산자에 배열-포인터 변환이 수행돼요. (C++26 이후)

내장 양방향 비교 연산자의 결과는 bool prvalue예요.

내장 산술 비교

변환된 피연산자가 둘 다 산술 또는 열거형 타입(scoped, unscoped 모두)이면 두 피연산자에 일반 산술 변환이 수행돼요. 변환 후 값을 비교해요:

#include <iostream>

int main()
{
    static_assert(sizeof(unsigned char) < sizeof(int),
                  "Cannot compare signed and smaller unsigned properly");
    int a = -1;
    int b = 1;
    unsigned int c = 1;
    unsigned char d = 1;

    std::cout << std::boolalpha
              << "Comparing two signed values:\n"
                 " -1 == 1 ? " << (a == b) << "\n"
                 " -1 <  1 ? " << (a <  b) << "\n"
                 " -1 >  1 ? " << (a >  b) << "\n"
                 "Comparing signed and unsigned:\n"
                 // may issue different-signedness warning:
                 " -1 == 1 ? " << (a == c) << "\n"
                 // may issue different-signedness warning:
                 " -1 <  1 ? " << (a <  c) << "\n"
                 // may issue different-signedness warning:
                 " -1 >  1 ? " << (a >  c) << "\n"
                 "Comparing signed and smaller unsigned:\n"
                 " -1 == 1 ? " << (a == d) << "\n"
                 " -1 <  1 ? " << (a <  d) << "\n"
                 " -1 >  1 ? " << (a >  d) << '\n';
}

출력:

Comparing two signed values:
 -1 == 1 ? false
 -1 <  1 ? true
 -1 >  1 ? false
Comparing signed and unsigned:
 -1 == 1 ? false
 -1 <  1 ? false
 -1 >  1 ? true
Comparing signed and smaller unsigned:
 -1 == 1 ? false
 -1 <  1 ? true
 -1 >  1 ? false

여기서 signedunsigned int를 비교하면 일반 산술 변환이 intunsigned int로 바꿔서 -1UINT_MAX처럼 커져요. 그래서 a < c(부호 있는 -1 < 부호 없는 1)가 false가 되고, a > ctrue가 돼요. 반면 unsigned chardint보다 작아서 정수 승격으로 int가 되므로, a < d가 올바르게 true예요. 부호 있는 값과 크기가 다른(작은) 부호 없는 타입을 비교할 때 특히 조심해야 하는 이유예요.

내장 포인터 동등 비교

동등 연산자 ==!=의 변환된 피연산자는 std::nullptr_t(C++11 이후), 포인터 타입, 멤버 포인터 타입도 가질 수 있어요.

내장 포인터 동등 비교에는 세 가지 가능한 결과가 있어요: 같음(equal), 다름(unequal), 불특정(unspecified). 동등 연산자가 내장 포인터 동등 비교에 대해 내는 값은 아래 표와 같아요:

p, q의 비교 결과 p==q가 내는 값 p!=q가 내는 값
같음 true false
다름 false true
불특정 불특정 bool 불특정 bool

변환된 lhsrhs 중 적어도 하나가 포인터면, 두 변환된 피연산자에 포인터 변환, 함수 포인터 변환(C++17 이후), 자격 변환(qualification conversion)을 수행해 복합 포인터 타입(composite pointer type)으로 맞춰요. 복합 포인터 타입의 두 포인터는 다음과 같이 비교돼요:

  • 한 포인터가 완전 객체의 주소를 나타내고 다른 포인터가:
    • 다른 완전 비배열 객체의 끝 뒤 주소를 나타내거나
    • 다른 완전 배열 객체의 마지막 요소 뒤 주소를 나타내면 — 비교 결과는 불특정이에요.
  • 그 외에 두 포인터가 모두 null이거나, 같은 함수를 가리키거나, 같은 주소를 나타내면(즉 같은 객체를 가리키거나 그 끝 뒤에 있으면) 같다고 비교돼요.
  • 그 외에는 다르다고 비교돼요.

변환된 lhsrhs 중 적어도 하나가 멤버 포인터면, 두 변환된 피연산자에 멤버 포인터 변환, 함수 포인터 변환(C++17 이후), 자격 변환을 수행해 복합 포인터 타입으로 맞춰요. 복합 포인터 타입의 두 멤버 포인터는 다음과 같이 비교돼요:

  • 두 멤버 포인터가 모두 null 멤버 포인터 값이면 같다고 비교돼요.
  • 두 멤버 포인터 중 하나만 null 멤버 포인터 값이면 다르다고 비교돼요.
  • 둘 중 하나가 가상 멤버 함수에 대한 포인터면 결과는 불특정이에요.
  • 하나가 클래스 C1의 멤버를 가리키고 다른 하나가 다른 클래스 C2(둘 다 서로의 기반 클래스가 아닌)의 멤버를 가리키면 결과는 불특정이에요.
  • 둘 다 같은 유니온의 (가능하면 다른) 멤버를 가리키면 같다고 비교돼요.
  • 그 외에 두 멤버 포인터는, 연관 클래스 타입의 가상 객체로 역참조(indirection)를 수행한다면 같은 가장 파생된 객체의 같은 멤버 또는 같은 부분객체를 가리킬 때 같다고 비교되고, 그 외에는 다르다고 비교돼요.
struct P {};
struct Q : P { int x; };
struct R : P { int x; };

int P::*bx = (int(P::*)) &Q::x;
int P::*cx = (int(P::*)) &R::x;

bool b1 = (bx == cx); // unspecified

struct B
{
    int f();
};
struct L : B {};
struct R : B {};
struct D : L, R {};

int (B::*pb)() = &B::f;
int (L::*pl)() = pb;
int (R::*pr)() = pb;
int (D::*pdl)() = pl;
int (D::*pdr)() = pr;

bool x = (pdl == pdr); // false
bool y = (pb == pl);   // true

std::nullptr_t 타입의 두 피연산자, 또는 한쪽이 std::nullptr_t이고 다른 쪽이 널 포인터 상수인 경우는 같다고 비교돼요. (C++11 이후)

내장 포인터 관계 비교

관계 연산자 >, <, >=, <=의 변환된 피연산자는 포인터 타입도 가질 수 있어요.

같지 않은 포인터 p, q에 대한 내장 포인터 관계 비교 결과는 세 가지가 가능해요: p가 더 큼, q가 더 큼, 불특정. 관계 연산자가 내는 값은 아래 표와 같아요:

p, q의 비교 결과 p>q p<q p>=q p<=q
같음 false false true true
p가 더 큼 true false true false
q가 더 큼 false true false true
불특정 불특정 bool 불특정 bool 불특정 bool 불특정 bool

변환된 lhsrhs가 둘 다 포인터면, 두 변환된 피연산자에 포인터 변환, 함수 포인터 변환(C++17 이후), 자격 변환을 수행해 복합 포인터 타입으로 맞춰요. 복합 포인터 타입의 두 포인터는 다음과 같이 비교돼요:

  • 포인터들이 같다고 비교되거나 동등 비교 결과가 불특정이면 관계 비교 결과도 같은 범주에 들어가요.
  • 그 외에 (포인터들이 다르다고 비교되는데) 포인터 중 하나라도 객체에 대한 포인터가 아니면 결과는 불특정이에요.
  • 그 외에 (두 포인터 모두 객체를 가리키는) 결과는 다음 규칙들과 일관된 부분 순서(partial order)로 정의돼요:
    • 배열의 highlow라는 서로 다른 두 요소(여기서 highlow보다 큰 첨자를 가짐)에 대해, 한 포인터가 high(또는 그 부분객체)를 가리키고 다른 포인터가 low(또는 그 부분객체)를 가리키면 전자가 후자보다 크다고 비교돼요.
    • 한 포인터가 배열의 요소 elem(또는 elem의 부분객체)을 가리키고 다른 포인터가 같은 배열의 끝 뒤에 있으면, 끝 뒤 포인터가 더 크다고 비교돼요.
    • 한 포인터가 완전 객체·기반 클래스 부분객체·멤버 부분객체 obj(또는 obj의 부분객체)를 가리키고 다른 포인터가 obj의 끝 뒤에 있으면, 끝 뒤 포인터가 더 크다고 비교돼요.
    • 포인터들이 non-union 클래스 타입의 같은 객체에 속한, 같은 멤버 접근 방식(until C++23)의 서로 다른 0이 아닌 크기(C++20 이후) 비정적 데이터 멤버(또는 재귀적으로 그런 멤버의 부분객체)를 가리키면, 나중에 선언된 멤버를 가리키는 포인터가 더 크다고 비교돼요.
    • 그 외에는 결과가 불특정이에요.

포인터 전체 순서 (Pointer total order)

각 프로그램에는 포인터에 대한 구현 정의의 **엄격한 전체 순서(strict total order)**가 존재해요. 이 전체 순서는 위에서 설명한 부분 순서와 일관돼요: 불특정 결과가 구현 정의가 되고, 다른 결과는 그대로 유지돼요.

엄격한 전체 순서로 포인터를 비교하는 경우는 다음과 같아요:

  • 포인터 타입 특수화인 std::less, std::greater, std::less_equal, std::greater_equaloperator() 호출
  • std::less<void>, std::greater<void>, std::less_equal<void>, std::greater_equal<void> 특수화의 operator()에서 포인터를 비교하는 내장 연산자를 호출하는 경우 (C++14 이후)
  • std::compare_three_wayoperator()에서 포인터를 비교하는 내장 operator<=> 호출
  • std::ranges::equal_to, std::ranges::not_equal_tooperator()에서 포인터를 비교하는 내장 operator== 호출
  • std::ranges::less, std::ranges::greater, std::ranges::less_equal, std::ranges::greater_equaloperator()에서 포인터를 비교하는 내장 operator< 호출 (C++20 이후)

내장 반영 비교 (Built-in reflection comparison)

std::meta::info 타입의 두 값은 다음 중 하나를 만족하면 같다고 비교돼요:

  • null 반영 값(reflection value)
  • 템플릿 인자와 동등한(template-argument-equivalent) 값을 표현
  • 같은 객체를 표현
  • 같은 개체(entity)를 표현
  • 같은 주석(annotation)을 표현
  • 같은 직접 기반 클래스 관계를 표현
  • 같은 데이터 멤버 설명을 표현

타입·네임스페이스 별칭의 반영은 그 별칭이 가리키는 타입·네임스페이스의 반영과 다르다고 비교된다는 점을 주의하세요. (C++26 이후)

오버로드 (양방향)

사용자 정의 연산자와의 오버로드 결정에서, 열거형 타입을 포함한 승격된 산술 타입의 모든 쌍 L, R에 대해 다음 함수 시그니처가 참여해요:

bool operator<(L, R);
bool operator>(L, R);
bool operator<=(L, R);
bool operator>=(L, R);
bool operator==(L, R);
bool operator!=(L, R);

객체 포인터나 함수 포인터인 모든 타입 P에 대해 다음 시그니처가 참여해요:

bool operator<(P, P);
bool operator>(P, P);
bool operator<=(P, P);
bool operator>=(P, P);
bool operator==(P, P);
bool operator!=(P, P);

멤버 객체 포인터·멤버 함수 포인터·std::nullptr_t(C++11 이후)·std::meta::info(C++26 이후)인 모든 타입 MP에 대해 다음 시그니처가 참여해요:

bool operator==(MP, MP);
bool operator!=(MP, MP);
#include <iostream>

struct Foo
{
    int n1;
    int n2;
};

union Union
{
    int n;
    double d;
};

int main()
{
    std::cout << std::boolalpha;

    char a[4] = "abc";
    char* p1 = &a[1];
    char* p2 = &a[2];
    std::cout << "Pointers to array elements:\n"
              << "p1 == p2? " << (p1 == p2) << '\n'
              << "p1 <  p2? " << (p1 <  p2) << '\n';

    Foo f;
    int* p3 = &f.n1;
    int* p4 = &f.n2;
    std::cout << "Pointers to members of a class:\n"
              << "p3 == p4? " << (p3 == p4) << '\n'
              << "p3 <  p4? " << (p3 <  p4) << '\n';

    Union u;
    int* p5 = &u.n;
    double* p6 = &u.d;
    std::cout << "Pointers to members of a union:\n"
              << "p5 == (void*)p6? " << (p5 == (void*)p6) << '\n'
              << "p5 <  (void*)p6? " << (p5 <  (void*)p6) << '\n';
}

출력:

Pointers to array elements:
p1 == p2? false
p1 <  p2? true
Pointers to members of a class:
p3 == p4? false
p3 <  p4? true
Pointers to members of a union:
p5 == (void*)p6? true
p5 <  (void*)p6? false

유니온의 멤버는 서로 겹치는 기억 공간을 차지하므로, 유니온의 서로 다른 멤버를 가리키는 포인터들은 일반적으로 같은 주소를 가져요. 그래서 p5 == (void*)p6true예요.

3방향 비교 (Three-way comparison)

3방향 비교 연산자 표현식은 다음 형태를 가져요:

a <=> b

이 표현식은 다음을 만족하는 객체를 돌려줘요:

  • a < b이면 (a <=> b) < 0
  • a > b이면 (a <=> b) > 0
  • ab가 같거나 동등(equivalent)이면 (a <=> b) == 0

피연산자 중 하나가 bool 타입이고 다른 하나가 아니면, 프로그램은 ill-formed예요.

두 피연산자 모두 산술 타입이거나, 한 피연산자가 unscoped 열거형 타입이고 다른 하나가 정수 타입이면, 피연산자에 일반 산술 변환이 적용된 다음:

  • 정수 타입에서 부동소수점 타입으로의 변환 외에 수렴 변환(narrowing conversion)이 필요하면 프로그램은 ill-formed예요.
  • 그렇지 않고 피연산자가 정수 타입이면 연산자는 std::strong_ordering 타입의 prvalue를 내요:
    • 두 피연산자가 산술적으로 같으면 std::strong_ordering::equal
    • 첫 번째 피연산자가 두 번째보다 산술적으로 작으면 std::strong_ordering::less
    • 그 외에는 std::strong_ordering::greater
  • 그렇지 않으면 피연산자는 부동소수점 타입이고, 연산자는 std::partial_ordering 타입의 prvalue를 내요. a <=> b는:
    • ab보다 작으면 std::partial_ordering::less
    • ab보다 크면 std::partial_ordering::greater
    • ab와 동등하면 std::partial_ordering::equivalent (-0 <=> +0은 동등)
    • std::partial_ordering::unordered (NaN <=> anything은 순서 없음)

두 피연산자가 같은 열거형 타입 E이면, 연산자는 피연산자들을 E의 기반 타입으로 변환하고 변환된 피연산자에 <=>를 적용한 결과를 내요.

피연산자 중 적어도 하나가 객체 포인터나 멤버 포인터이면, 두 피연산자에 배열-포인터 변환, 포인터 변환, 자격 변환을 적용해 복합 포인터 타입으로 맞춰요.

변환된 포인터 피연산자 p, q에 대해 p <=> qstd::strong_ordering 타입의 prvalue를 돌려줘요:

  • 같다고 비교되면 std::strong_ordering::equal
  • qp보다 크다고 비교되면 std::strong_ordering::less
  • pq보다 크다고 비교되면 std::strong_ordering::greater
  • 양방향 비교 결과가 불특정이면 불특정 결과

그 외에는 프로그램이 ill-formed예요.

오버로드 (3방향)

사용자 정의 연산자와의 오버로드 결정에서, 포인터나 열거형 타입 T에 대해 다음 함수 시그니처가 참여해요 (여기서 R은 위에서 정의한 정렬 범주 타입이에요):

R operator<=>(T, T);
#include <compare>
#include <iostream>

int main()
{
    double foo = -0.0;
    double bar = 0.0;

    if (const auto res = foo <=> bar; res < 0)
        std::cout << "-0 is less than 0";
    else if (res > 0)
        std::cout << "-0 is greater than 0";
    else if (res == 0)
        std::cout << "-0 and 0 are equal";
    else
        std::cout << "-0 and 0 are unordered";
}

출력:

-0 and 0 are equal

부동소수점 3방향 비교에서 -0.0+0.0equivalent로 간주돼요. (C++20 이후)

참고 (Notes)

비교 연산자는 왼쪽에서 오른쪽으로 결합하기 때문에, a < b < ca < (b < c)(a < b) && (b < c)가 아니라 (a < b) < c로 파싱돼요.

#include <iostream>

int main()
{
    int a = 3, b = 2, c = 1;

    std::cout << std::boolalpha
        << (a < b < c) << '\n' // true; maybe warning
        << ((a < b) < c) << '\n' // true
        << (a < (b < c)) << '\n' // false
        << ((a < b) && (b < c)) << '\n'; // false
}

a < b < c(a < b) < c로 파싱되는데, a < bfalse(0)이고 0 < 1은 참이라 true가 나와요. 세 값을 제대로 비교하려면 (a < b) && (b < c)처럼 써야 해요.

사용자 정의 operator<에 대한 흔한 요구사항은 **엄격한 약순서(strict weak ordering)**예요. 특히 std::sort, std::max_element, std::map처럼 Compare 타입으로 동작하는 표준 알고리즘과 컨테이너가 이걸 요구해요.

같은 클래스의 서로 다른 비정적 데이터 멤버를 가리키는 포인터의 비교 결과는, 각 멤버 접근 방식(until C++23)의 비정적 데이터 멤버가 선언 순서대로 메모리에 배치된다는 걸 암시해요.

임의 기원의 포인터(예: 모두 같은 배열 멤버를 가리키지 않는) 비교 결과는 불특정이지만, 많은 구현이 포인터에 엄격한 전체 순서를 제공해요 (예: 연속된 가상 주소 공간 안의 주소로 구현된 경우). 그렇게 하지 않는 구현(예: 포인터의 모든 비트가 메모리 주소의 일부가 아니어서 비교 시 무시해야 하거나, 추가 계산이 필요해 포인터와 정수가 1:1 관계가 아닌 경우)은 그 보장을 제공하는 std::less의 포인터 특수화를 제공해요. 이 덕분에 임의 기원의 모든 포인터를 std::set이나 std::map 같은 표준 연관 컨테이너의 키로 쓸 수 있어요.

EqualityComparable과 LessThanComparable을 모두 만족하는 타입에 대해 C++ 표준 라이브러리는 **동등(equality)**와 **등가(equivalence)**를 구분해요. 동등은 a == b 표현식의 값이고, 등가는 !(a < b) && !(b < a) 표현식의 값이에요.

포인터와 널 포인터 상수 사이의 비교는 N3624에 포함된 CWG issue 583의 해결로 제거됐어요:

void f(char* p)
{
    if (p > 0) { /*...*/ } // Error with N3624, compiled before N3624
    if (p > nullptr) { /*...*/ } // Error with N3624, compiled before N3624
}

int main() {}

클래스 타입에 대해서는 3방향 비교를 자동으로 생성할 수 있어요. 기본 비교(default comparisons)를 참고해요.

두 피연산자 모두 배열이면 3방향 비교는 ill-formed예요.

unsigned int i = 1;
auto r = -1 < i;    // existing pitfall: returns 'false'
auto r2 = -1 <=> i; // Error: narrowing conversion required

-1 < i에서 부호 있는 -1unsigned int로 변환돼 커져서 false가 나와요(이미 알려진 함정). 반면 <=>은 수렴 변환이 필요해서 컴파일 오류가 나요.

기능 테스트 매크로 표준 기능
__cpp_impl_three_way_comparison 201907L (C++20) 3방향 비교 (컴파일러 지원)
__cpp_lib_three_way_comparison 201907L (C++20) 3방향 비교 (라이브러리 지원); 3방향 비교를 라이브러리에 추가

표준 라이브러리

비교 연산자는 표준 라이브러리의 많은 클래스에 대해 오버로드되어 있어요. 대표적으로: std::type_info, std::error_code, std::pair, std::tuple, std::bitset, std::allocator, std::unique_ptr, std::shared_ptr, std::chrono::duration·time_point, std::type_index, 문자열, std::locale, 배열·deque·list·vector·map·set 등 컨테이너, std::queue·stack, 반복자, std::complex, std::valarray, 난수 엔진·분포, std::regex 관련, std::thread::id 등이 있어요. (C++20에서 많은 이항 비교 연산자가 <=> 기반으로 재정의되면서 일부 ==/!=/< 등이 제거됐어요.)

네임스페이스 std::rel_ops는 일반적인 !=, >, <=, >= 연산자를 제공해요:

  • <utility> 헤더에 정의되어 있고 std::rel_ops 네임스페이스에 있어요.
  • operator!=, operator>, operator<=, operator>= — 사용자 정의 operator==operator<에 기반해 비교 연산자를 자동으로 생성해요. (C++20에서 폐기 예정)

결함 보고 (Defect reports)

다음은 이전에 발행된 C++ 표준에 소급 적용된, 동작을 바꾸는 결함 보고예요.

DR 적용 대상 발행 당시 동작 올바른 동작
CWG 583 (N3624) C++98 여섯 비교 연산자 모두 포인터와 널 포인터 상수를 비교하는 데 쓸 수 있었음 동등 연산자만 허용
CWG 661 C++98 산술 비교의 실제 의미론(예: 1 < 2true인지 false인지)이 불특정이었음 명세 추가
CWG 879 C++98 함수 타입 포인터와 void 포인터에 내장 비교가 없었음 이 포인터들에 비교 명세 추가
CWG 1596 C++98 비배열 객체는 포인터 산술 목적으로만 크기 1 배열에 속하는 것으로 간주됨 규칙이 비교에도 적용됨
CWG 1598 C++98 서로 다른 클래스(둘 다 서로의 기반 클래스가 아닌)의 두 멤버 포인터는 가리키는 멤버의 오프셋이 같아도 같다고 비교되지 않았음 이 경우 결과는 불특정
CWG 1858 C++98 같은 유니온의 서로 다른 멤버를 가리키는 두 멤버 포인터가 같은 멤버를 가리키는 것처럼 같다고 비교되는지 불명확했음 이 경우 같다고 비교됨
CWG 2419 C++98 비배열 객체에 대한 포인터는 &로 얻은 경우에만 포인터 비교에서 크기 1 배열의 첫 요소 포인터로 취급됨 비배열 객체에 대한 모든 포인터에 적용됨
CWG 2526 C++98 void 포인터와 함수 포인터의 관계 비교(>, >=, <, <=) 정의가 N3624로 제거됨 복원됨
CWG 2796 C++17 내장 포인터 관계 비교 동안 변환된 포인터 피연산자에 함수 포인터 변환이 수행되지 않았음 이 경우 변환 수행

더 알아보기 (Learn more)

  • 연산자 우선순위 — 각 연산자의 우선순위와 결합성을 정리한 표예요.
  • 연산자 오버로딩 — 사용자 정의 타입에서 연산자를 재정의하는 방법이에요.
  • 기본 비교(default comparisons) — 클래스 타입에 <=>를 자동 생성하는 방법이에요.
  • Compare (named requirements) — Compare 타입이 만족해야 하는 요구사항이에요.