비교 연산자
비교 연산자 (Comparison operators)
비교 연산자는 인자를 서로 비교해요. 같은지, 다른지, 큰지 작은지 — 여기에 C++20의 3방향 비교(<=>)까지 묶어서 다루는 주제예요.
본문
| 연산자 이름 | 문법 | 오버로드 가능 | 클래스 안 프로토타입 예 (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);¹ |
¹R은 operator<=>의 반환 타입이에요 (아래 참고).
참고:
- 내장 연산자가
bool을 돌려주는 곳에서 대부분의 사용자 정의 오버로드도 내장처럼 쓰일 수 있도록bool을 돌려줘요. 다만 사용자 정의 오버로드에서는 반환 타입으로 어떤 타입이든(void포함) 쓸 수 있어요. U는T를 포함해 어떤 타입이든 될 수 있어요.
양방향 비교 (Two-way comparison)
양방향 비교 연산자 표현식은 다음 형태를 가져요.
관계 연산자 (Relational operators):
| 형태 | 설명 |
|---|---|
lhs < rhs |
(1) lhs가 rhs보다 작으면 true, 그 외에는 false |
lhs > rhs |
(2) lhs가 rhs보다 크면 true, 그 외에는 false |
lhs <= rhs |
(3) lhs가 rhs보다 작거나 같으면 true, 그 외에는 false |
lhs >= rhs |
(4) lhs가 rhs보다 크거나 같으면 true, 그 외에는 false |
동등 연산자 (Equality operators):
| 형태 | 설명 |
|---|---|
lhs == rhs |
(5) lhs가 rhs와 같으면 true, 그 외에는 false |
lhs != rhs |
(6) lhs가 rhs와 같지 않으면 true, 그 외에는 false |
내장 양방향 비교 연산자
내장 양방향 비교 연산자에서 lhs와 rhs에 lvalue-to-rvalue 변환, 배열-포인터 변환(until C++26), 함수-포인터 변환이 적용돼요.
이 변환들을 적용하기 전에 lhs와 rhs가 둘 다 배열 타입이면 그 비교는 폐기 예정이에요. (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
여기서 signed와 unsigned int를 비교하면 일반 산술 변환이 int를 unsigned int로 바꿔서 -1이 UINT_MAX처럼 커져요. 그래서 a < c(부호 있는 -1 < 부호 없는 1)가 false가 되고, a > c가 true가 돼요. 반면 unsigned char인 d는 int보다 작아서 정수 승격으로 int가 되므로, a < d가 올바르게 true예요. 부호 있는 값과 크기가 다른(작은) 부호 없는 타입을 비교할 때 특히 조심해야 하는 이유예요.
내장 포인터 동등 비교
동등 연산자 ==와 !=의 변환된 피연산자는 std::nullptr_t(C++11 이후), 포인터 타입, 멤버 포인터 타입도 가질 수 있어요.
내장 포인터 동등 비교에는 세 가지 가능한 결과가 있어요: 같음(equal), 다름(unequal), 불특정(unspecified). 동등 연산자가 내장 포인터 동등 비교에 대해 내는 값은 아래 표와 같아요:
p, q의 비교 결과 |
p==q가 내는 값 |
p!=q가 내는 값 |
|---|---|---|
| 같음 | true |
false |
| 다름 | false |
true |
| 불특정 | 불특정 bool 값 |
불특정 bool 값 |
변환된 lhs와 rhs 중 적어도 하나가 포인터면, 두 변환된 피연산자에 포인터 변환, 함수 포인터 변환(C++17 이후), 자격 변환(qualification conversion)을 수행해 복합 포인터 타입(composite pointer type)으로 맞춰요. 복합 포인터 타입의 두 포인터는 다음과 같이 비교돼요:
- 한 포인터가 완전 객체의 주소를 나타내고 다른 포인터가:
- 다른 완전 비배열 객체의 끝 뒤 주소를 나타내거나
- 다른 완전 배열 객체의 마지막 요소 뒤 주소를 나타내면 — 비교 결과는 불특정이에요.
- 그 외에 두 포인터가 모두 null이거나, 같은 함수를 가리키거나, 같은 주소를 나타내면(즉 같은 객체를 가리키거나 그 끝 뒤에 있으면) 같다고 비교돼요.
- 그 외에는 다르다고 비교돼요.
변환된 lhs와 rhs 중 적어도 하나가 멤버 포인터면, 두 변환된 피연산자에 멤버 포인터 변환, 함수 포인터 변환(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 값 |
변환된 lhs와 rhs가 둘 다 포인터면, 두 변환된 피연산자에 포인터 변환, 함수 포인터 변환(C++17 이후), 자격 변환을 수행해 복합 포인터 타입으로 맞춰요. 복합 포인터 타입의 두 포인터는 다음과 같이 비교돼요:
- 포인터들이 같다고 비교되거나 동등 비교 결과가 불특정이면 관계 비교 결과도 같은 범주에 들어가요.
- 그 외에 (포인터들이 다르다고 비교되는데) 포인터 중 하나라도 객체에 대한 포인터가 아니면 결과는 불특정이에요.
- 그 외에 (두 포인터 모두 객체를 가리키는) 결과는 다음 규칙들과 일관된 부분 순서(partial order)로 정의돼요:
- 배열의
high와low라는 서로 다른 두 요소(여기서high는low보다 큰 첨자를 가짐)에 대해, 한 포인터가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_equal의operator()호출 std::less<void>,std::greater<void>,std::less_equal<void>,std::greater_equal<void>특수화의operator()에서 포인터를 비교하는 내장 연산자를 호출하는 경우 (C++14 이후)std::compare_three_way의operator()에서 포인터를 비교하는 내장operator<=>호출std::ranges::equal_to,std::ranges::not_equal_to의operator()에서 포인터를 비교하는 내장operator==호출std::ranges::less,std::ranges::greater,std::ranges::less_equal,std::ranges::greater_equal의operator()에서 포인터를 비교하는 내장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*)p6이 true예요.
3방향 비교 (Three-way comparison)
3방향 비교 연산자 표현식은 다음 형태를 가져요:
a <=> b
이 표현식은 다음을 만족하는 객체를 돌려줘요:
a < b이면(a <=> b) < 0a > b이면(a <=> b) > 0a와b가 같거나 동등(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는:a가b보다 작으면std::partial_ordering::lessa가b보다 크면std::partial_ordering::greatera가b와 동등하면std::partial_ordering::equivalent(-0 <=> +0은 동등)std::partial_ordering::unordered(NaN <=> anything은 순서 없음)
두 피연산자가 같은 열거형 타입 E이면, 연산자는 피연산자들을 E의 기반 타입으로 변환하고 변환된 피연산자에 <=>를 적용한 결과를 내요.
피연산자 중 적어도 하나가 객체 포인터나 멤버 포인터이면, 두 피연산자에 배열-포인터 변환, 포인터 변환, 자격 변환을 적용해 복합 포인터 타입으로 맞춰요.
변환된 포인터 피연산자 p, q에 대해 p <=> q는 std::strong_ordering 타입의 prvalue를 돌려줘요:
- 같다고 비교되면
std::strong_ordering::equal q가p보다 크다고 비교되면std::strong_ordering::lessp가q보다 크다고 비교되면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.0은 equivalent로 간주돼요. (C++20 이후)
참고 (Notes)
비교 연산자는 왼쪽에서 오른쪽으로 결합하기 때문에, a < b < c는 a < (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 < b가 false(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에서 부호 있는 -1이 unsigned 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 < 2가 true인지 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타입이 만족해야 하는 요구사항이에요.