C++ 삼원 비교 연산자 | "Spaceship Operator" 가이드

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 |