C++20의 새로운 기능들을 소개하는 시리즈의 첫 번째 글에 오신 것을 환영합니다. 이번 글에서는 프로그래머들의 코딩을 더욱 간결하고 효율적으로 만들어 줄 삼항 비교 연산자(<=>), 일명 우주선 연산자에 대해 알아보겠습니다.
삼항 비교 연산자(<=>)란?
C++20에서 도입된 삼항 비교 연산자(<=>)는 두 객체의 비교를 하나의 연산으로 처리할 수 있게 해주는 강력한 도구입니다. 이 연산자를 사용하면 객체의 <, >, ==, !=, <=, >= 연산자를 하나하나 구현할 필요 없이, 자동으로 생성된 비교 연산자를 활용할 수 있습니다.
왜 <=>를 사용해야 할까요?
기존에는 사용자 정의 객체를 비교할 때 모든 비교 연산자를 수동으로 구현해야 했습니다. 이는 코드의 중복을 초래하고 유지 보수를 어렵게 만들었습니다. <=> 연산자를 사용하면 이러한 문제를 해결하고, 코드의 가독성과 효율성을 높일 수 있습니다.
간단한 예제
기존 방식의 비교 연산자 구현
class Point {
public:
int x, y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
bool operator!=(const Point& other) const {
return !(*this == other);
}
bool operator<(const Point& other) const {
if (x != other.x)
return x < other.x;
return y < other.y;
}
// 다른 비교 연산자들도 추가로 구현해야 합니다.
};
위 코드에서는 Point 클래스의 객체들을 비교하기 위해 여러 연산자들을 직접 구현했습니다.
<=> 연산자를 사용한 개선
#include <compare>
class Point {
public:
int x, y;
auto operator<=>(const Point& other) const = default;
};
이제 <=> 연산자를 = default로 선언하면, 컴파일러가 자동으로 모든 비교 연산자들을 생성해줍니다.
<=> 연산자의 작동 방식
1. 자동 생성된 operator<=>의 동작
operator<=>를 = default로 선언하면, 컴파일러는 클래스의 멤버들을 선언된 순서대로 비교하여 연산자를 자동 생성합니다.
auto operator<=>(const Point& other) const = default;
위 코드에서 컴파일러는 다음과 같이 동작합니다:
- this->x와 other.x를 비교합니다.
- x 값이 같지 않으면 그 결과를 반환합니다.
- x 값이 같으면 this->y와 other.y를 비교합니다.
- 모든 멤버에 대한 비교 결과를 종합하여 최종 결과를 반환합니다.
2. 반환 타입과 비교 카테고리
operator<=>의 반환 타입은 비교되는 멤버들의 타입에 따라 결정됩니다. 일반적으로 기본 자료형(int, double 등)은 std::strong_ordering을 반환합니다.
- std::strong_ordering: 완전한 순서와 동치성을 강하게 보장합니다.
- std::weak_ordering: 동치성은 약하게, 순서는 강하게 보장합니다.
- std::partial_ordering: 부분적인 순서만 보장하며, 비교 불가능한 경우를 허용합니다.
3. 자동 생성되는 비교 연산자들
operator<=>를 = default로 선언하면 다음의 연산자들이 자동으로 생성됩니다:
- operator==
- operator!=
- operator<
- operator<=
- operator>
- operator>=
예제 코드로 살펴보기
#include <compare>
#include <iostream>
class Point {
public:
int x, y;
auto operator<=>(const Point& other) const = default;
};
int main() {
Point p1{1, 2};
Point p2{2, 3};
if (p1 < p2) {
std::cout << "p1이 p2보다 작습니다." << std::endl;
} else if (p1 == p2) {
std::cout << "p1과 p2는 같습니다." << std::endl;
} else {
std::cout << "p1이 p2보다 큽니다." << std::endl;
}
return 0;
}
작동 원리 상세 설명
p1 < p2를 평가할 때:
- p1.operator<=>(p2)가 호출됩니다.
- p1.x와 p2.x를 비교합니다: 1 <=> 2 결과는 std::strong_ordering::less입니다.
- 첫 번째 멤버 비교에서 이미 less 결과가 나왔으므로, p1이 p2보다 작다고 판단합니다.
- 따라서 if (p1 < p2) 조건이 참이 됩니다.
p1 == p2를 평가할 때:
- 자동 생성된 operator==를 사용합니다.
- p1.x == p2.x 결과는 false입니다.
- 따라서 p1과 p2는 같지 않습니다.
멤버 비교 순서의 중요성
멤버 비교는 선언된 순서대로 이루어집니다. 만약 비교 우선순위를 변경하고 싶다면 멤버 선언 순서를 바꾸거나, operator<=>를 직접 구현해야 합니다.
class Point {
public:
int x, y;
// y를 먼저 비교하도록 직접 구현
auto operator<=>(const Point& other) const {
if (auto cmp = y <=> other.y; cmp != 0)
return cmp;
return x <=> other.x;
}
};
위 코드에서는 y를 먼저 비교하도록 operator<=>를 직접 구현했습니다.
operator<=>를 직접 구현하는 경우
특정한 비교 로직이 필요할 때는 operator<=>를 직접 구현할 수 있습니다.
class Person {
public:
std::string name;
int age;
// 나이로만 비교
auto operator<=>(const Person& other) const {
return age <=> other.age;
}
// 이름과 나이를 모두 비교하여 동치성 판단
bool operator==(const Person& other) const {
return name == other.name && age == other.age;
}
};
주의 사항
- 반환 타입 명시: 직접 구현할 때는 반환 타입을 명시하는 것이 좋습니다.
- 동치성 연산자: operator==는 자동 생성되지 않으므로, 필요하다면 직접 구현해야 합니다.
상세 정리
- 자동 생성 조건: 클래스의 모든 멤버들이 비교 가능해야 operator<=>를 = default로 선언할 수 있습니다.
- 비교 불가능한 멤버: 비교할 수 없는 멤버가 있다면 operator<=>를 직접 구현해야 합니다.
- 동치성 판단: operator<=>를 = default로 선언하면 operator==도 자동으로 생성됩니다.
- 멤버의 비교 순서: 멤버들이 선언된 순서대로 비교되므로, 순서를 신경 써야 합니다.
결론
삼항 비교 연산자(<=>)와 = default를 사용하면 객체의 비교 연산을 매우 간단하게 구현할 수 있습니다. 이는 코드의 양을 줄이고, 유지 보수를 용이하게 하며, 실수를 줄이는 데 큰 도움이 됩니다.
'개발 이야기 > C++' 카테고리의 다른 글
[C++20 새기능 소개] 지정 초기화자(Designated Initializers) (19) | 2024.11.30 |
---|---|
[C++20 새기능 소개] 모듈 (Modules) (0) | 2024.11.29 |
[C++20 새기능 소개] 코루틴 (Coroutines) (0) | 2024.11.28 |
[C++20 새기능 소개] 범위 (Ranges) 라이브러리 (0) | 2024.11.27 |
[C++20 새기능 소개] 개념 (Concepts) (0) | 2024.11.27 |