C++ 삼원 비교 연산자 | "Spaceship Operator" 가이드
이 글의 핵심
C++ 삼원 비교 연산자에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.
Spaceship Operator란?
C++20의 삼원 비교 연산자 <=>
struct Point {
int x, y;
// C++20 이전: 6개 연산자 필요
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;
}
// <=, >, >= 생략...
// C++20: 1개로 모두 생성
auto operator<=>(const Point&) const = default;
};
반환 타입
#include <compare>
// strong_ordering: 완전 순서
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
// <, <=, >, >=, ==, != 모두 생성
};
// weak_ordering: 동등하지만 구별 가능
struct CaseInsensitiveString {
string str;
weak_ordering operator<=>(const CaseInsensitiveString& other) const {
// 대소문자 무시 비교
return strcasecmp(str.c_str(), other.str.c_str()) <=> 0;
}
};
// partial_ordering: 부분 순서 (NaN 등)
struct Float {
double value;
partial_ordering operator<=>(const Float& other) const {
return value <=> other.value;
}
};
default 비교
struct Person {
string name;
int age;
// 모든 멤버를 순서대로 비교
auto operator<=>(const Person&) const = default;
};
Person p1{"Alice", 30};
Person p2{"Bob", 25};
cout << (p1 < p2) << endl; // name 비교
cout << (p1 == p2) << endl; // name과 age 모두 비교
실전 예시
예시 1: 기본 사용
struct Student {
string name;
int score;
auto operator<=>(const Student&) const = default;
};
int main() {
Student s1{"Alice", 90};
Student s2{"Bob", 85};
cout << (s1 < s2) << endl; // 0 (Alice < Bob는 false)
cout << (s1 > s2) << endl; // 1
cout << (s1 == s2) << endl; // 0
}
예시 2: 커스텀 비교
struct Student {
string name;
int score;
// score로만 비교
strong_ordering operator<=>(const Student& other) const {
return score <=> other.score;
}
// == 는 별도 정의 필요
bool operator==(const Student& other) const {
return score == other.score;
}
};
int main() {
Student s1{"Alice", 90};
Student s2{"Bob", 85};
cout << (s1 > s2) << endl; // 1 (90 > 85)
vector<Student> students = {
{"Charlie", 75},
{"Alice", 90},
{"Bob", 85}
};
sort(students.begin(), students.end());
for (const auto& s : students) {
cout << s.name << ": " << s.score << endl;
}
// Charlie: 75
// Bob: 85
// Alice: 90
}
예시 3: 복합 비교
struct Product {
string category;
string name;
double price;
// category -> name -> price 순으로 비교
auto operator<=>(const Product& other) const {
if (auto cmp = category <=> other.category; cmp != 0)
return cmp;
if (auto cmp = name <=> other.name; cmp != 0)
return cmp;
return price <=> other.price;
}
bool operator==(const Product& other) const = default;
};
예시 4: 대소문자 무시
struct CaseInsensitiveString {
string str;
weak_ordering operator<=>(const CaseInsensitiveString& other) const {
auto toLower = {
transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
};
return toLower(str) <=> toLower(other.str);
}
bool operator==(const CaseInsensitiveString& other) const {
return (*this <=> other) == 0;
}
};
int main() {
CaseInsensitiveString s1{"Hello"};
CaseInsensitiveString s2{"hello"};
cout << (s1 == s2) << endl; // 1
}
비교 카테고리
// strong_ordering: 완전 순서
// a < b, a == b, a > b 중 정확히 하나
int a = 1, b = 2;
auto cmp1 = a <=> b; // strong_ordering::less
// weak_ordering: 동등하지만 구별 가능
// "Hello"와 "hello"는 동등하지만 다름
weak_ordering cmp2 = weak_ordering::equivalent;
// partial_ordering: 부분 순서
// NaN은 어떤 값과도 비교 불가
double x = 1.0, y = NAN;
auto cmp3 = x <=> y; // partial_ordering::unordered
자주 발생하는 문제
문제 1: == 생성 안됨
// ❌ == 자동 생성 안됨
struct Point {
int x, y;
auto operator<=>(const Point& other) const {
return x <=> other.x; // y 무시
}
};
Point p1{1, 2}, p2{1, 3};
// p1 == p2; // 컴파일 에러
// ✅ == 명시적 정의
struct Point {
int x, y;
auto operator<=>(const Point& other) const {
return x <=> other.x;
}
bool operator==(const Point& other) const = default;
};
문제 2: 타입 불일치
// ❌ 다른 타입 비교
struct A {
int value;
auto operator<=>(const A&) const = default;
};
struct B {
int value;
auto operator<=>(const B&) const = default;
};
A a{1};
B b{1};
// a == b; // 컴파일 에러
// ✅ 변환 연산자 또는 명시적 비교
문제 3: 포인터 비교
// ❌ 포인터 주소 비교
struct Node {
int value;
Node* next;
auto operator<=>(const Node&) const = default;
// next는 주소로 비교됨
};
// ✅ 커스텀 비교
struct Node {
int value;
Node* next;
auto operator<=>(const Node& other) const {
return value <=> other.value;
}
};
default vs 커스텀
// default: 모든 멤버 비교
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
};
// 커스텀: 특정 멤버만 비교
struct Point {
int x, y;
auto operator<=>(const Point& other) const {
return x <=> other.x; // x만 비교
}
bool operator==(const Point&) const = default;
};
FAQ
Q1: Spaceship Operator는 언제 사용하나요?
A:
- 정렬 가능한 타입
- 비교 연산자 간소화
- C++20 이상
Q2: 모든 연산자 생성?
A: <, <=, >, >= 생성. ==는 별도 정의 필요 (default 가능).
Q3: 성능은?
A: 수동 구현과 동일. 컴파일러 최적화 가능.
Q4: C++20 이전에는?
A: 6개 연산자 수동 구현.
Q5: 반환 타입 선택은?
A:
- strong_ordering: 완전 순서
- weak_ordering: 동등 관계
- partial_ordering: 부분 순서
Q6: Spaceship Operator 학습 리소스는?
A:
- “C++20 The Complete Guide”
- cppreference.com
- “Effective Modern C++“
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Range Adaptor | “범위 어댑터” 가이드
- C++ Template Lambda | “템플릿 람다” 가이드
- C++ Concepts와 Constraints | “타입 제약” 가이드
관련 글
- C++ Barrier & Latch |
- C++ Branch Prediction |
- C++ Calendar & Timezone |
- 모던 C++ (C++11~C++20) 핵심 문법 치트시트 | 현업에서 자주 쓰는 한눈에 보기
- C++ Concepts와 Constraints |