Default comparisons (since C++20)
비교 연산자 함수를 명시적으로 기본값으로 설정하여 컴파일러가 클래스에 대한 해당 기본 비교를 생성하도록 요청할 수 있습니다.
목차 |
정의
디폴트 비교 연산자 함수
는 다음 모든 조건을 만족하는 비템플릿 비교 연산자 함수입니다(즉
<=>
,
==
,
!=
,
<
,
>
,
<=
, 또는
>=
):
-
이것은 어떤 클래스
C의 비정적 멤버 함수 또는 friend 함수 입니다. -
이것이
C내부에서 또는C가 완전한 타입 인 문맥에서 기본 정의됨 으로 선언되었습니다. -
이것은
const
C
&
타입의 두 매개변수를 가지거나
C타입의 두 매개변수를 가지며, 여기서 암시적 객체 매개변수 (존재하는 경우)가 첫 번째 매개변수로 간주됩니다.
이러한 비교 연산자 함수를
클래스
C
에 대한 기본 비교 연산자 함수
라고 합니다.
struct X { bool operator==(const X&) const = default; // 정상 bool operator==(const X&) = default; // 오류: 암시적 객체 // 매개변수 타입은 X& bool operator==(this X, X) = default; // 정상 }; struct Y { friend bool operator==(Y, Y) = default; // 정상 friend bool operator==(Y, const Y&) = default; // 오류: 서로 다른 매개변수 타입 }; bool operator==(const Y&, const Y&) = default; // 오류: Y의 friend가 아님
비교 연산자 함수의 암시적 정의에서의 이름 조회와 접근 검사는 해당 함수 본문과 동등한 컨텍스트에서 수행됩니다. 클래스 내에서 기본값으로 정의된 비교 연산자 함수의 선언은 해당 함수의 첫 번째 선언이어야 합니다.
기본 비교 순서
주어진 클래스
C
에 대해, 서브오브젝트 목록은 다음과 같은 서브오브젝트들로 순서대로 구성됩니다:
-
C의 직접 기본 클래스 서브오브젝트를 선언 순서대로. -
C의 비정적 데이터 멤버 를 선언 순서대로.
-
- 어떤 멤버 하위 객체가 배열 타입인 경우, 해당 배열의 요소들을 오름차순 인덱스 순서로 확장합니다. 이 확장은 재귀적으로 수행됩니다: 배열 타입의 배열 요소들은 더 이상 배열 타입의 하위 객체가 없을 때까지 다시 확장됩니다.
임의의 객체
x
가 타입
C
인 경우, 다음 설명에서:
- n 을 (확장된) 서브오브젝트 목록에서의 서브오브젝트 개수로 둡니다.
- x_i 를 (확장된) 서브오브젝트 목록에서 i 번째 서브오브젝트로 둡니다. 여기서 x_i 는 x 에 적용된 일련의 파생-대-기본 변환 , 클래스 멤버 접근 표현식 , 그리고 배열 첨자 표현식 으로 구성됩니다.
struct S {}; struct T : S { int arr[2][2]; } t; // "t"의 서브오브젝트 목록은 다음과 같이 5개의 서브오브젝트로 순서대로 구성됩니다: // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
3항 비교
클래스 타입에 대한 operator <=> 는 어떤 반환 타입으로도 기본 정의될 수 있습니다.
비교 범주 유형
비교 범주 유형은 세 가지가 있습니다:
| 타입 | 동등한 값들은.. | 비교 불가능한 값들은.. |
|---|---|---|
| std::strong_ordering | 구분 불가능함 | 허용되지 않음 |
| std::weak_ordering | 구분 가능함 | 허용되지 않음 |
| std::partial_ordering | 구분 가능함 | 허용됨 |
합성된 삼중 비교
T
타입의
합성된 삼중 비교
는 동일한 타입의 glvalue
a
와
b
사이에서 다음과 같이 정의됩니다:
-
a
<=>
b
에 대한 오버로드 해결이 사용 가능한 후보를 산출하고,
static_cast를 사용하여T로 명시적으로 변환될 수 있는 경우, 합성된 비교는 static_cast < T > ( a <=> b ) 입니다. - 그렇지 않고 다음 조건 중 하나라도 충족되는 경우, 합성된 비교는 정의되지 않습니다:
-
- a <=> b 에 대한 오버로드 해결이 적어도 하나의 유효한 후보를 찾습니다.
-
T는 비교 범주 타입이 아닙니다. - a == b 에 대한 오버로드 해결이 사용 가능한 후보를 결과로 내지 않습니다.
- a < b 에 대한 오버로드 해결이 사용 가능한 후보를 결과로 내지 않습니다.
-
그렇지 않고, 만약
T가 std::strong_ordering 라면, 합성된 비교는
a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater
-
그렇지 않고, 만약
T가 std::weak_ordering 라면, 합성된 비교 연산은
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
-
그 외의 경우 (
T가 std::partial_ordering 인 경우), 합성된 비교는
a == b ? std::partial_ordering::equivalent : a < b ? std::partial_ordering::less : b < a ? std::partial_ordering::greater : std::partial_ordering::unordered
플레이스홀더 반환 타입
클래스 타입
C
의 기본화된 삼중 비교 연산자 함수(
operator
<=>
)의 선언된 반환 타입이
auto
인 경우, 반환 타입은 타입
C
의 객체
x
의 해당 하위 객체들 간의 삼중 비교 연산 결과 타입들로부터 추론됩니다.
각 하위 객체 x_i 에 대해 (확장된) 하위 객체 목록 내에서 x 의:
- x_i <=> x_i 에 대해 오버로드 해결을 수행합니다. 오버로드 해결이 사용 가능한 후보를 생성하지 않으면, 기본 제공 operator <=> 는 삭제된 것으로 정의됩니다.
-
x_i
<=>
x_i
의 타입에서 cv 한정자를 제거한 버전을
R_i로 표기합니다. 만약R_i가 비교 범주 타입이 아니면, 기본 제공 operator <=> 는 삭제된 것으로 정의됩니다.
기본값으로 정의된 operator <=> 가 삭제된 것으로 정의되지 않으면, 그 반환 타입은 std:: common_comparison_category_t < R_1, R_2, ..., R_n > 로 추론됩니다.
Non-placeholder 반환 타입
기본 설정된 operator <=> 의 선언된 반환 타입이 auto 가 아닌 경우, 이는 어떠한 placeholder type (예: decltype ( auto ) )도 포함할 수 없습니다.
(확장된) 서브오브젝트 목록에 x_i 가 존재하고, 선언된 반환 타입의 합성된 3-way 비교 가 x_i 와 x_i 사이에 정의되지 않는다면, 기본 정의된 operator <=> 는 삭제된 것으로 정의됩니다.
비교 결과
기본 설정된 operator <=> 의 매개변수를 x 와 y 라 하고, x 와 y 의 (확장된) 서브객체 목록에 있는 각 서브객체를 각각 x_i 와 y_i 로 표기한다. x 와 y 사이의 기본 삼중 비교는 대응하는 서브객체 x_i 와 y_i 를 i 가 증가하는 순서로 비교하여 수행된다.
R
을 (추론된) 반환 타입이라고 하면,
x_i
와
y_i
사이의 비교 결과는
x_i
와
y_i
사이의 타입
R
의 합성된 삼중 비교 결과입니다.
- 기본적인 3방향 비교에서 x 와 y 사이의 부분 객체별 비교 시, x_i 와 y_i 의 비교 결과 v_i 가 v_i ! = 0 를 bool 로 문맥 변환했을 때 true 를 반환하는 경우, 반환값은 v_i 의 복사본입니다(나머지 부분 객체들은 비교되지 않습니다).
- 그렇지 않은 경우, 반환값은 static_cast < R > ( std :: strong_ordering :: equal ) 입니다.
#include <compare> #include <iostream> #include <set> struct Point { int x; int y; auto operator<=>(const Point&) const = default; /* 비교 함수가 아닌 다른 함수들 */ }; int main() { Point pt1{1, 1}, pt2{1, 2}; std::set<Point> s; // OK s.insert(pt1); // OK // 양방향 비교 연산자 함수들은 명시적으로 정의될 필요가 없음: // operator==는 암시적으로 선언됨 (아래 참조) // 다른 후보들의 오버로드 해석은 재작성된 후보들을 선택함 std::cout << std::boolalpha << (pt1 == pt2) << ' ' // false << (pt1 != pt2) << ' ' // true << (pt1 < pt2) << ' ' // true << (pt1 <= pt2) << ' ' // true << (pt1 > pt2) << ' ' // false << (pt1 >= pt2) << ' '; // false }
동등성 비교
명시적 선언
클래스 타입에 대한 operator == 는 반환 타입이 bool 인 기본 정의로 선언될 수 있습니다.
클래스
C
와 해당 타입의 객체
x
가 주어졌을 때,
x
의 (확장된) 서브객체 목록에 있는 서브객체
x_i
에 대해
x_i
==
x_i
의 오버로드 해결이 사용 가능한 후보를 결과로 내지 않으면, 기본 제공되는
operator
==
는 삭제된 것으로 정의됩니다.
기본 설정된 operator == 의 매개변수인 x 와 y 가 있을 때, (확장된) 서브객체 목록에 있는 각 서브객체를 각각 x_i 와 y_i 로 표기합니다. x 와 y 사이의 기본 동등 비교는 대응하는 서브객체 x_i 와 y_i 를 i 가 증가하는 순서로 비교하여 수행됩니다.
x_i 와 y_i 의 비교 결과는 x_i == y_i 의 결과입니다.
- 기본 동등 비교에서 x 와 y 사이의 부분 객체 단위 비교 시, x_i 와 y_i 의 비교 결과 v_i 를 bool 로 문맥적 변환했을 때 false 가 생성되면, 반환 값은 false 가 됩니다(나머지 부분 객체는 비교되지 않음).
- 그렇지 않으면 반환 값은 true 입니다.
#include <iostream> struct Point { int x; int y; bool operator==(const Point&) const = default; /* 비교 함수가 아닌 다른 함수들 */ }; int main() { Point pt1{3, 5}, pt2{2, 5}; std::cout << std::boolalpha << (pt1 != pt2) << '\n' // true << (pt1 == pt1) << '\n'; // true struct [[maybe_unused]] { int x{}, y{}; } p, q; // if (p == q) {} // 오류: operator==가 정의되지 않음 }
암시적 선언
클래스
C
가
operator
==
라는 이름의 멤버나 friend를 명시적으로 선언하지 않는 경우, 기본 설정(defaulted)으로 정의된 각
operator
<=>
에 대해
==
연산자 함수가 암시적으로 선언됩니다. 각 암시적으로 선언된
operator
==
는 해당 기본 설정
operator
<=>
와 동일한 접근 권한과
함수 정의
를 가지며 동일한
클래스 범위
에 존재하며, 다음과 같은 변경 사항이 적용됩니다:
- 선언자 식별자가 operator == 로 대체됩니다.
- 반환 타입이 bool 로 대체됩니다.
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // 암시적으로 선언: friend constexpr bool operator==(X, X) // requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // 암시적으로 선언: [[nodiscard]] virtual bool // operator==(const X&) const = default; };
보조 비교
클래스 타입에 대한 보조 비교 연산자 함수(
!=
,
<
,
>
,
<=
, 또는
>=
)는 반환 타입
bool
으로 기본 정의될 수 있습니다.
다섯 가지 보조 비교 연산자 중 하나인
@
에 대해, 각 기본 제공
operator@
는 매개변수
x
와
y
를 가지며, 삭제된 것으로 정의되는지 여부를 결정하기 위해 최대 두 번의 오버로드 해결이 수행됩니다(기본 제공
operator@
는 후보로 고려하지 않음).
- 첫 번째 오버로드 해결은 x @ y 에 대해 수행됩니다. 오버로드 해결이 사용 가능한 후보를 생성하지 않거나 선택된 후보가 재작성된 후보 가 아닌 경우, 기본 제공 operator@ 는 삭제된 것으로 정의됩니다. 이러한 경우에는 두 번째 오버로드 해결이 수행되지 않습니다.
- 두 번째 오버로드 해결은 x @ y 의 선택된 재작성된 후보에 대해 수행됩니다. 오버로드 해결이 사용 가능한 후보를 생성하지 않는 경우, 기본 제공 operator@ 는 삭제된 것으로 정의됩니다.
만약 x @ y 가 암시적으로 bool 으로 변환될 수 없다면, 기본 제공되는 operator@ 는 삭제된 것으로 정의됩니다.
기본으로 제공되는 operator@ 가 삭제된 것으로 정의되지 않으면, x @ y 를 반환합니다.
struct HasNoRelational {}; struct C { friend HasNoRelational operator<=>(const C&, const C&); bool operator<(const C&) const = default; // OK, 함수가 기본값으로 설정됨 };
키워드
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2539 | C++20 |
합성된 3방향 비교가 명시적 변환이 불가능한 경우에도
static_cast 를 선택함 |
해당 경우
static_cast 를 선택하지 않음 |
| CWG 2546 | C++20 |
기본 제공된 보조
operator@
가
x @ y 의 오버로드 해결이 사용 불가능한 재작성 후보를 선택할 경우 삭제된 것으로 정의되지 않음 |
해당 경우
삭제된 것으로 정의됨 |
| CWG 2547 | C++20 |
비클래스에 대한 비교 연산자 함수의
기본 제공 가능 여부가 불명확했음 |
기본 제공할 수 없음 |
| CWG 2568 | C++20 |
비교 연산자 함수의 암시적 정의가
멤버 접근 규칙을 위반할 수 있었음 |
해당 함수 본문과 동등한
컨텍스트에서 접근 검사가 수행됨 |
참고 항목
- 오버로드된 연산자 호출에서의 오버로드 해결
- 내장 3-way 비교 연산자
- 비교 연산자에 대한 연산자 오버로딩