C++20에서 새롭게 도입된 spaceship operator(<=>) 는 객체 비교 연산을 단순화하는 강력한 기능입니다. 기존 C++에서는 비교 연산자(==, !=, <, >, <=, >=)를 직접 오버로딩해야 했으며, 모든 연산자를 구현하는 과정에서 중복 코드가 발생하는 문제가 있었습니다.
spaceship operator는 이러한 불편함을 해결하여 단 한 줄의 코드만으로 비교 연산자를 자동 생성할 수 있도록 합니다. 이를 통해 코드 가독성이 높아지고 유지보수가 쉬워지며, 표준 라이브러리와의 연동도 더욱 직관적으로 이루어집니다.
이 기사에서는 spaceship operator의 개념과 구현 방법, 비교 연산 자동 생성 원리, 사용자 정의 객체에 적용하는 방법을 자세히 살펴봅니다. 또한, 기존 비교 연산 방식과의 차이를 분석하고, std::sort 등의 표준 알고리즘에서 spaceship operator를 활용하는 방법도 함께 다룹니다.
spaceship operator란?
C++20에서 새롭게 추가된 spaceship operator(<=>) 는 객체 간의 비교 연산을 간결하게 처리하는 기능을 제공합니다. 기존 C++에서는 ==, !=, <, >, <=, >= 등의 비교 연산자를 각각 구현해야 했으나, spaceship operator를 활용하면 단 한 줄의 코드만으로 모든 비교 연산을 자동으로 생성할 수 있습니다.
spaceship operator의 기본 문법
spaceship operator는 세 방향 비교(three-way comparison) 를 수행하는 연산자로, 다음과 같은 형태로 사용됩니다.
#include <iostream>
#include <compare> // spaceship operator를 사용하기 위해 필요
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // spaceship operator 사용
};
int main() {
Point p1{2, 3}, p2{2, 5};
if (p1 < p2) {
std::cout << "p1이 p2보다 작습니다.\n";
} else {
std::cout << "p1이 p2보다 크거나 같습니다.\n";
}
return 0;
}
위 코드에서 operator<=>
를 정의하면 ==, !=, <, >, <=, >=
연산자가 자동으로 생성됩니다. 따라서, 비교 연산자를 일일이 오버로딩할 필요 없이 간편하게 객체 비교가 가능합니다.
spaceship operator의 반환값
spaceship operator는 비교 결과를 std::strong_ordering
, std::weak_ordering
, std::partial_ordering
과 같은 타입으로 반환합니다. 이는 비교 연산의 강도(strength)를 결정하며, 이에 대한 자세한 내용은 후속 섹션에서 다루겠습니다.
spaceship operator를 활용하면 코드의 간결성, 유지보수성, 성능 최적화를 모두 향상할 수 있습니다. 다음 섹션에서는 기존 비교 연산자의 한계를 살펴보고 spaceship operator의 장점을 더욱 깊이 이해해 보겠습니다.
기존 비교 연산자의 한계
C++20 이전까지 객체 간 비교를 수행하려면 모든 비교 연산자(==, !=, <, >, <=, >=)를 직접 오버로딩해야 했습니다. 이러한 방식은 코드 중복이 많아지고 유지보수가 어려워지는 단점이 있었습니다.
모든 비교 연산자를 직접 구현해야 하는 문제
예를 들어, C++17까지는 아래와 같이 각 비교 연산자를 개별적으로 정의해야 했습니다.
#include <iostream>
struct Point {
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 {
return x < other.x || (x == other.x && y < other.y);
}
bool operator>(const Point& other) const {
return other < *this;
}
bool operator<=(const Point& other) const {
return !(other < *this);
}
bool operator>=(const Point& other) const {
return !(*this < other);
}
};
int main() {
Point p1{2, 3}, p2{2, 5};
if (p1 < p2) {
std::cout << "p1이 p2보다 작습니다.\n";
}
}
위 코드에서는 operator==
, operator!=
, operator<
, operator>
, operator<=
, operator>=
를 모두 정의해야 합니다.
이는 비교 연산자가 많아질수록 코드 중복이 심각해지고, 새로운 필드가 추가될 경우 모든 연산자를 수정해야 하는 문제가 발생합니다.
유지보수의 어려움
- 새로운 비교 기준이 필요하면 모든 비교 연산자를 수정해야 합니다.
- 코드 중복으로 인해 버그 발생 가능성이 증가합니다.
- 개발자가 실수로
operator!=
또는operator<=
등을 정의하지 않으면 비교 연산이 올바르게 동작하지 않을 수 있습니다.
이러한 문제점을 해결하기 위해 C++20에서는 spaceship operator(<=>) 가 도입되었습니다. spaceship operator를 사용하면 한 줄의 코드만으로 모든 비교 연산자를 자동 생성할 수 있어 코드 중복을 줄이고 유지보수를 쉽게 할 수 있습니다. 다음 섹션에서는 spaceship operator의 구현 방식과 그 장점을 자세히 살펴보겠습니다.
spaceship operator의 구현 방식
C++20의 spaceship operator(<=>) 를 사용하면 단 한 줄의 코드만으로 모든 비교 연산자를 자동으로 생성할 수 있습니다. 기존의 비교 연산자 오버로딩 방식과 달리, spaceship operator는 한 번만 정의하면 ==, !=, <, >, <=, >= 연산자를 자동 생성하는 장점이 있습니다.
기본적인 spaceship operator 구현
아래는 spaceship operator를 사용한 비교 연산자 오버로딩 예제입니다.
#include <iostream>
#include <compare> // spaceship operator를 사용하기 위해 필요
struct Point {
int x, y;
// spaceship operator 적용
auto operator<=>(const Point&) const = default;
};
int main() {
Point p1{3, 4}, p2{3, 5};
if (p1 < p2) {
std::cout << "p1이 p2보다 작습니다.\n";
}
if (p1 == p2) {
std::cout << "p1과 p2는 같습니다.\n";
}
return 0;
}
위 코드에서 operator<=>
를 default
로 선언하면 모든 비교 연산자(==, !=, <, >, <=, >=)가 자동으로 생성됩니다. 이를 통해 코드 중복이 제거되며, 새로운 멤버 변수를 추가할 때도 비교 연산자를 따로 수정할 필요가 없습니다.
수동으로 spaceship operator 정의하기
= default
를 사용하지 않고 직접 spaceship operator를 구현할 수도 있습니다.
#include <iostream>
#include <compare>
struct Point {
int x, y;
// spaceship operator를 직접 정의
std::strong_ordering operator<=>(const Point& other) const {
if (auto cmp = x <=> other.x; cmp != 0) return cmp;
return y <=> other.y;
}
};
int main() {
Point p1{3, 4}, p2{3, 5};
if (p1 < p2) {
std::cout << "p1이 p2보다 작습니다.\n";
}
if (p1 == p2) {
std::cout << "p1과 p2는 같습니다.\n";
}
return 0;
}
위 코드에서는 x
좌표를 먼저 비교하고, 값이 동일하면 y
좌표를 비교하도록 spaceship operator를 직접 정의했습니다. 이를 통해 사용자가 원하는 비교 로직을 자유롭게 설정할 수 있습니다.
기본 제공되는 비교 타입
spaceship operator는 비교 연산의 성격에 따라 세 가지 타입을 지원합니다.
비교 타입 | 의미 | 반환 타입 |
---|---|---|
강한 순서 (Strong Ordering) | 완전한 순서 비교가 가능할 때 | std::strong_ordering |
약한 순서 (Weak Ordering) | 동등한 값이 있는 경우 비교 가능할 때 | std::weak_ordering |
부분 순서 (Partial Ordering) | 비교가 항상 가능하지 않을 때 | std::partial_ordering |
각 비교 타입의 차이점은 이후 섹션에서 더 자세히 다룹니다.
spaceship operator 도입의 장점
- 코드 중복 제거:
operator==
,operator!=
,operator<
,operator>
,operator<=
,operator>=
를 개별적으로 정의할 필요가 없습니다. - 자동 비교 연산 생성: 단 한 줄의 코드(
auto operator<=> = default;
)만으로 모든 비교 연산이 가능해집니다. - 가독성과 유지보수성 향상: 새로운 필드를 추가해도 비교 연산자를 수정할 필요가 없습니다.
- 성능 최적화: 컴파일러가 최적화된 비교 연산을 자동으로 생성하여 성능이 향상될 수 있습니다.
다음 섹션에서는 spaceship operator가 자동으로 생성하는 비교 연산자들의 동작 원리를 살펴보겠습니다.
비교 연산 자동 생성의 원리
C++20에서 spaceship operator(<=>
)를 사용하면 한 줄의 코드만으로 비교 연산자가 자동 생성됩니다. 기존에는 ==
, !=
, <
, >
, <=
, >=
연산자를 각각 정의해야 했지만, spaceship operator를 적용하면 자동으로 필요한 비교 연산자가 생성됩니다.
자동 생성되는 비교 연산자
spaceship operator는 다음 규칙에 따라 비교 연산자를 자동 생성합니다.
operator<=>
만 정의하면==
,!=
,<
,>
,<=
,>=
가 자동 생성됨.operator==
만 정의하면!=
이 자동 생성됨.operator<=>
와operator==
를 함께 정의하면 모든 비교 연산자가 자동 생성됨.
예제: 기본적인 자동 생성 원리
#include <iostream>
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // spaceship operator 적용
};
int main() {
Point p1{3, 4}, p2{3, 5};
std::cout << std::boolalpha;
std::cout << "p1 == p2: " << (p1 == p2) << "\n"; // 자동 생성된 == 연산자 사용
std::cout << "p1 < p2: " << (p1 < p2) << "\n"; // 자동 생성된 < 연산자 사용
}
출력 결과:
p1 == p2: false
p1 < p2: true
위 코드에서는 operator<=>
를 default
로 정의했기 때문에 모든 비교 연산자가 자동으로 생성됩니다.
연산자 자동 생성 규칙
spaceship operator가 적용되었을 때 자동으로 생성되는 연산자들의 관계는 아래 표와 같습니다.
정의된 연산자 | 자동 생성되는 연산자 |
---|---|
operator<=> | == , != , < , > , <= , >= |
operator== | != |
operator<=> + operator== | 모든 비교 연산자 |
이 원리를 활용하면 코드 중복을 최소화하면서도 모든 비교 연산을 지원할 수 있습니다.
예제: operator<=>
없이 operator==
만 정의할 경우
#include <iostream>
struct Point {
int x, y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
};
int main() {
Point p1{3, 4}, p2{3, 4};
std::cout << std::boolalpha;
std::cout << "p1 == p2: " << (p1 == p2) << "\n"; // 직접 정의한 ==
std::cout << "p1 != p2: " << (p1 != p2) << "\n"; // 자동 생성된 !=
}
출력 결과:
p1 == p2: true
p1 != p2: false
위 코드에서는 operator==
만 정의했지만 !=
연산자가 자동으로 생성되었습니다. 그러나 <
, >
, <=
, >=
연산자는 정의되지 않기 때문에 컴파일 오류가 발생합니다. 이를 방지하려면 operator<=>
를 사용해야 합니다.
비교 연산 자동 생성의 장점
- 코드 유지보수 간편화
- 한 줄의 코드(
auto operator<=> = default;
)만 추가하면 모든 비교 연산이 자동 생성됩니다. - 새로운 멤버 변수를 추가해도 비교 연산자를 다시 작성할 필요가 없습니다.
- 중복 코드 감소
- 기존 방식에서는
==
,!=
,<
,>
,<=
,>=
를 모두 직접 구현해야 했습니다. - spaceship operator를 사용하면 불필요한 코드 중복을 줄이고 가독성을 높일 수 있습니다.
- 성능 최적화
- 컴파일러가 자동으로 비교 연산을 생성하므로 최적화된 코드가 생성될 가능성이 높아집니다.
요약
- spaceship operator를 사용하면
operator<=>
만 정의해도 모든 비교 연산자를 자동 생성할 수 있습니다. operator==
만 정의하면!=
연산자만 자동 생성되며,<
,>
,<=
,>=
는 자동 생성되지 않습니다.- 코드 중복을 줄이고 유지보수를 쉽게 만들며, 컴파일러의 최적화를 유도할 수 있습니다.
다음 섹션에서는 spaceship operator에서 지원하는 강한 순서, 약한 순서, 부분 순서 비교(strong_ordering, weak_ordering, partial_ordering) 에 대해 자세히 살펴보겠습니다.
강한, 약한, 부분 순서 비교
C++20의 spaceship operator(<=>
) 는 객체 간의 비교 연산을 단순화할 뿐만 아니라, 비교 연산의 성질에 따라 강한 순서(strong ordering), 약한 순서(weak ordering), 부분 순서(partial ordering) 를 지원합니다. 각 순서의 차이를 이해하면, 특정 데이터 구조나 연산에 적절한 비교 방식을 적용할 수 있습니다.
비교 순서의 개념
비교 순서 | 의미 | 사용 예시 |
---|---|---|
강한 순서 (Strong Ordering) | 완벽한 순서가 존재하며, a == b 면 반드시 동일한 값을 가짐. | 정수형, 문자열 비교 |
약한 순서 (Weak Ordering) | a == b 일 때 논리적으로 동등하지만, 내부적으로 다를 수 있음. | 사용자 정의 객체 비교 |
부분 순서 (Partial Ordering) | 일부 값 간에는 비교가 불가능할 수 있음. | 부동소수점 NaN 비교 |
1. 강한 순서 비교 (`std::strong_ordering`)
강한 순서(strong ordering) 는 모든 비교 연산자가 일관된 결과를 보장하는 경우 사용됩니다. 즉, a == b
이면 반드시 같은 값을 가지며, <
, >
, <=
, >=
연산도 항상 논리적으로 올바른 결과를 반환합니다.
#include <iostream>
#include <compare>
struct IntWrapper {
int value;
auto operator<=>(const IntWrapper&) const = default; // strong_ordering 사용
};
int main() {
IntWrapper a{5}, b{10};
if (a < b) std::cout << "a가 b보다 작음\n";
if (a == a) std::cout << "a와 a는 같음\n";
}
출력 결과:
a가 b보다 작음
a와 a는 같음
- 정수형
int
,char
등의 기본 타입은std::strong_ordering
을 따릅니다. = default
를 사용하면 강한 순서 비교가 기본 적용됩니다.
2. 약한 순서 비교 (`std::weak_ordering`)
약한 순서(weak ordering) 는 a == b
일 때 내부적으로 다를 수 있음을 의미합니다. 예를 들어, 두 개의 다른 객체가 논리적으로는 같지만 내부적으로 미묘한 차이가 있을 때 사용할 수 있습니다.
#include <iostream>
#include <compare>
struct Person {
std::string name;
int age;
std::weak_ordering operator<=>(const Person& other) const {
return age <=> other.age; // 나이 기준 비교
}
};
int main() {
Person p1{"Alice", 30}, p2{"Bob", 30};
if (p1 == p2) std::cout << "두 사람은 같은 나이입니다.\n";
}
출력 결과:
두 사람은 같은 나이입니다.
p1
과p2
는 이름이 다르지만 같은 나이이므로 논리적으로는 같다고 판단합니다.- 약한 순서는 논리적으로 동등하지만 완전히 동일하지는 않은 경우에 사용됩니다.
3. 부분 순서 비교 (`std::partial_ordering`)
부분 순서(partial ordering) 는 일부 값에 대해 비교가 불가능할 수도 있는 경우에 사용됩니다. 대표적인 예가 부동소수점 NaN(Not-a-Number) 비교입니다.
#include <iostream>
#include <compare>
#include <cmath>
struct FloatWrapper {
double value;
std::partial_ordering operator<=>(const FloatWrapper& other) const {
if (std::isnan(value) || std::isnan(other.value))
return std::partial_ordering::unordered; // 비교 불가능
return value <=> other.value;
}
};
int main() {
FloatWrapper a{NAN}, b{5.0};
if (a < b) std::cout << "a가 b보다 작음\n";
else if (a > b) std::cout << "a가 b보다 큼\n";
else std::cout << "a와 b는 비교할 수 없음\n";
}
출력 결과:
a와 b는 비교할 수 없음
NAN
값이 포함되면<=>
연산이 비교 불가능(unordered) 상태를 반환합니다.- 부분 순서는 비교할 수 없는 값이 존재하는 경우에 사용됩니다.
비교 순서 정리
비교 방식 | 의미 | 예제 |
---|---|---|
강한 순서 (std::strong_ordering ) | 모든 비교 연산이 항상 유효 | 정수, 문자열 |
약한 순서 (std::weak_ordering ) | 동등한 값이 내부적으로 다를 수 있음 | 객체 비교 (나이 같은 사람) |
부분 순서 (std::partial_ordering ) | 일부 값 간의 비교가 불가능할 수도 있음 | 부동소수점 NaN |
spaceship operator와 비교 타입의 조합
spaceship operator를 사용할 때, 비교 연산의 타입을 명확히 설정하면 코드의 의도를 더욱 정확하게 표현할 수 있습니다.
예제: 비교 타입 명시적으로 설정
#include <iostream>
#include <compare>
struct Data {
int x;
std::strong_ordering operator<=>(const Data&) const = default;
};
struct User {
std::string name;
int score;
std::weak_ordering operator<=>(const User& other) const {
return score <=> other.score;
}
};
int main() {
Data d1{10}, d2{20};
User u1{"Alice", 90}, u2{"Bob", 90};
if (d1 < d2) std::cout << "d1이 d2보다 작음 (강한 순서)\n";
if (u1 == u2) std::cout << "u1과 u2는 같은 점수 (약한 순서)\n";
}
출력 결과:
d1이 d2보다 작음 (강한 순서)
u1과 u2는 같은 점수 (약한 순서)
Data
구조체는std::strong_ordering
을 사용하여 완전한 비교가 가능합니다.User
구조체는std::weak_ordering
을 사용하여 점수만 비교하고 이름은 무시합니다.
요약
- 강한 순서 (strong ordering): 모든 비교 연산이 항상 유효함. (ex. 정수, 문자열 비교)
- 약한 순서 (weak ordering): 동등한 값이 내부적으로 다를 수 있음. (ex. 나이가 같은 사람)
- 부분 순서 (partial ordering): 일부 값에 대해 비교가 불가능할 수도 있음. (ex. 부동소수점 NaN 비교)
C++20의 spaceship operator는 비교 연산을 자동 생성할 뿐만 아니라, 비교 순서의 성격까지 명확하게 지정할 수 있도록 지원합니다. 이를 활용하면 더 안전하고 직관적인 비교 연산을 구현할 수 있습니다.
다음 섹션에서는 사용자 정의 클래스에서 spaceship operator를 적용하는 방법을 살펴보겠습니다.
사용자 정의 클래스에서 spaceship operator 적용하기
C++20의 spaceship operator(<=>
)는 기본 타입뿐만 아니라 사용자 정의 클래스에도 쉽게 적용할 수 있습니다. 이를 통해 객체 간의 비교 연산을 효율적으로 처리할 수 있으며, 직접 비교 연산자를 구현하는 번거로움을 줄일 수 있습니다. 이 섹션에서는 사용자 정의 클래스에 spaceship operator를 적용하는 방법을 다룹니다.
기본 구조체에 spaceship operator 적용하기
먼저, 기본적인 사용자 정의 구조체에 spaceship operator를 적용하여 비교 연산을 자동으로 생성하는 방법을 설명합니다.
#include <iostream>
#include <compare>
struct Point {
int x, y;
// spaceship operator를 사용하여 자동으로 비교 연산자 생성
auto operator<=>(const Point&) const = default;
};
int main() {
Point p1{3, 4}, p2{3, 5}, p3{3, 4};
std::cout << std::boolalpha;
std::cout << "p1 == p2: " << (p1 == p2) << "\n"; // 자동 생성된 == 연산자
std::cout << "p1 != p3: " << (p1 != p3) << "\n"; // 자동 생성된 != 연산자
std::cout << "p1 < p2: " << (p1 < p2) << "\n"; // 자동 생성된 < 연산자
}
출력 결과:
p1 == p2: false
p1 != p3: false
p1 < p2: true
auto operator<=> = default;
를 사용하면==
,!=
,<
,>
,<=
,>=
연산자가 자동으로 생성됩니다.- 위 예제에서는
Point
구조체를 정의하고,operator<=>
를default
로 지정하여 모든 비교 연산자가 자동 생성됩니다.
사용자 정의 비교 기준 적용하기
때로는 비교 연산자가 객체의 특정 멤버 변수나 속성을 기준으로 정의되어야 할 경우가 있습니다. 이런 경우, spaceship operator를 커스터마이즈하여 원하는 방식으로 비교를 할 수 있습니다.
#include <iostream>
#include <compare>
struct Rectangle {
int width, height;
// 비례 비교 기준으로 높이와 너비를 고려
auto operator<=>(const Rectangle& other) const {
return (width * height) <=> (other.width * other.height); // 면적 비교
}
};
int main() {
Rectangle r1{10, 20}, r2{15, 15}, r3{10, 20};
std::cout << std::boolalpha;
std::cout << "r1 == r2: " << (r1 == r2) << "\n"; // 면적 비교
std::cout << "r1 < r2: " << (r1 < r2) << "\n"; // 면적 비교
}
출력 결과:
r1 == r2: false
r1 < r2: true
Rectangle
구조체에서는 면적을 기준으로 두 사각형을 비교합니다.operator<=>
를 커스터마이즈하여width * height
를 비교 기준으로 사용합니다.
부분 순서 비교 적용하기
부분 순서 비교는 비교가 불가능할 수도 있는 값이 있을 때 사용됩니다. 예를 들어, NaN
이나 다른 정의되지 않은 상태를 처리해야 할 경우 부분 순서를 적용할 수 있습니다. 이를 사용자 정의 클래스에 적용하는 방법을 보여줍니다.
#include <iostream>
#include <compare>
#include <cmath>
struct Temperature {
double value;
// NaN을 고려한 부분 순서 비교
std::partial_ordering operator<=>(const Temperature& other) const {
if (std::isnan(value) || std::isnan(other.value))
return std::partial_ordering::unordered; // NaN 비교 불가
return value <=> other.value; // 정상적인 비교
}
};
int main() {
Temperature t1{36.6}, t2{37.0}, t3{NAN};
std::cout << std::boolalpha;
std::cout << "t1 == t2: " << (t1 == t2) << "\n"; // 비교 가능
std::cout << "t1 < t2: " << (t1 < t2) << "\n"; // 비교 가능
std::cout << "t1 == t3: " << (t1 == t3) << "\n"; // NaN 비교 불가
}
출력 결과:
t1 == t2: false
t1 < t2: true
t1 == t3: false
Temperature
구조체에서NaN
값을 비교할 때는std::partial_ordering::unordered
를 반환하여 비교 불가능을 처리합니다.
객체 포인터 비교
포인터를 비교하는 경우에도 spaceship operator를 적용할 수 있습니다. 포인터의 비교는 기본적으로 주소 값을 기준으로 비교됩니다.
#include <iostream>
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // spaceship operator 적용
};
int main() {
Point p1{1, 2}, p2{3, 4};
Point* ptr1 = &p1;
Point* ptr2 = &p2;
if (ptr1 < ptr2) std::cout << "ptr1이 ptr2보다 작음 (주소 비교)\n";
if (ptr1 == ptr2) std::cout << "ptr1과 ptr2는 같은 주소\n";
}
출력 결과:
ptr1이 ptr2보다 작음 (주소 비교)
- 포인터의 경우 주소 값을 기준으로 비교되며, 객체 자체의 값이 아닌 메모리 주소가 비교 대상이 됩니다.
요약
- 기본 비교 연산자: spaceship operator를 사용하여 자동으로
==
,!=
,<
,>
,<=
,>=
연산자를 생성할 수 있습니다. - 비교 기준 커스터마이즈: 비교 연산자가 특정 멤버 변수나 계산된 값을 기준으로 동작하도록 정의할 수 있습니다.
- 부분 순서: NaN과 같은 비교가 불가능한 값을 처리하기 위해
std::partial_ordering
을 사용할 수 있습니다. - 포인터 비교: 포인터를 비교할 때는 주소 값 기준으로 비교가 수행됩니다.
사용자 정의 클래스에 spaceship operator를 적용하면 코드가 간결해지고, 여러 비교 연산자를 효율적으로 처리할 수 있습니다.
spaceship operator와 std::sort 활용
C++20의 spaceship operator(<=>
)를 활용하면 객체 비교 연산이 간소화될 뿐만 아니라, 표준 알고리즘(std::sort
등)과의 연동도 쉬워집니다. 기존에는 std::sort
를 사용할 때 operator<
를 반드시 정의해야 했지만, spaceship operator를 사용하면 한 줄의 코드만으로 정렬이 가능해집니다.
std::sort와 spaceship operator 사용하기
아래 예제에서는 spaceship operator를 사용하여 std::sort
가 사용자 정의 객체를 올바르게 정렬하는지 확인합니다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Point {
int x, y;
// spaceship operator 적용
auto operator<=>(const Point&) const = default;
};
int main() {
std::vector<Point> points = {{3, 4}, {1, 2}, {5, 1}, {2, 3}};
// std::sort는 기본적으로 operator< 를 사용하므로 <=> 연산자가 있으면 자동 적용됨
std::sort(points.begin(), points.end());
// 정렬된 결과 출력
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ")\n";
}
}
출력 결과:
(1, 2)
(2, 3)
(3, 4)
(5, 1)
std::sort()
는 기본적으로operator<
를 요구하지만,operator<=>
가 정의된 경우 자동으로 이를 사용하여 정렬합니다.auto operator<=> = default;
를 선언하면operator<
가 자동 생성되어std::sort
와 자연스럽게 연동됩니다.
사용자 지정 정렬 기준 적용하기
기본 정렬 기준이 아닌 사용자 지정 기준으로 정렬하고 싶다면, std::sort
에 람다 함수 또는 명시적인 비교 연산자 함수를 전달할 수 있습니다.
1. 람다 함수 활용
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Point {
int x, y;
// spaceship operator 적용
auto operator<=>(const Point&) const = default;
};
int main() {
std::vector<Point> points = {{3, 4}, {1, 2}, {5, 1}, {2, 3}};
// y 좌표 기준 정렬 (람다 함수 사용)
std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) {
return a.y < b.y;
});
// 정렬된 결과 출력
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ")\n";
}
}
출력 결과:
(5, 1)
(1, 2)
(2, 3)
(3, 4)
- 기본적으로
std::sort
는<=>
연산자를 사용하여 정렬하지만, 특정 기준(예:y
값 기준 정렬)이 필요할 때 람다 함수를 활용할 수 있습니다.
2. 사용자 지정 비교 연산자 제공
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Point {
int x, y;
// spaceship operator 적용
auto operator<=>(const Point&) const = default;
};
// 정렬 기준을 사용자 지정하는 비교 함수 객체
struct CompareByY {
bool operator()(const Point& a, const Point& b) const {
return a.y < b.y; // y 값 기준으로 정렬
}
};
int main() {
std::vector<Point> points = {{3, 4}, {1, 2}, {5, 1}, {2, 3}};
// 사용자 지정 비교 함수 객체를 이용한 정렬
std::sort(points.begin(), points.end(), CompareByY());
// 정렬된 결과 출력
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ")\n";
}
}
- 비교 연산을 함수 객체(
CompareByY
)로 캡슐화하여 가독성을 높일 수 있습니다. - 람다 함수와 비교했을 때 코드의 재사용성이 높아집니다.
정렬 기준이 여러 개일 경우
spaceship operator를 활용하면 여러 개의 기준을 사용하여 정렬할 수도 있습니다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Point {
int x, y;
// spaceship operator 직접 정의 (x를 먼저 비교, 같으면 y 비교)
std::strong_ordering operator<=>(const Point& other) const {
if (auto cmp = x <=> other.x; cmp != 0) return cmp;
return y <=> other.y;
}
};
int main() {
std::vector<Point> points = {{3, 4}, {1, 2}, {5, 1}, {2, 3}, {3, 1}, {3, 5}};
std::sort(points.begin(), points.end()); // x 값 우선 비교, x가 같으면 y 비교
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ")\n";
}
}
출력 결과:
(1, 2)
(2, 3)
(3, 1)
(3, 4)
(3, 5)
(5, 1)
x
값을 우선 비교하고,x
값이 같으면y
값을 비교하는 방식으로 정렬됩니다.operator<=>
를 직접 정의하여 두 개의 기준을 한 번에 적용할 수 있습니다.
요약
- spaceship operator가 있으면
std::sort
와 자연스럽게 연동됩니다. - 기본적으로
operator<=>
를 정의하면 모든 비교 연산이 자동 생성되므로std::sort
사용이 간편해집니다. - 사용자 지정 정렬 기준이 필요하면 람다 함수나 비교 연산자 객체를 사용하여 적용할 수 있습니다.
- 복합 정렬 기준이 필요한 경우,
operator<=>
를 커스터마이즈하여 다중 필드를 비교할 수 있습니다.
C++20의 spaceship operator를 활용하면 코드를 더욱 간결하게 유지하면서도 유연한 정렬 기능을 손쉽게 구현할 수 있습니다.
유용한 예제와 실습 문제
C++20의 spaceship operator는 여러 가지 상황에서 유용하게 사용될 수 있습니다. 이 섹션에서는 실제 코드 예제를 통해 spaceship operator를 어떻게 활용할 수 있는지 보여주고, 실습 문제를 통해 학습 내용을 강화해보겠습니다.
예제 1: 3D 벡터 비교
3D 벡터 객체를 비교하는 문제에서 spaceship operator를 사용하여 코드를 간결하게 만드는 방법을 보여줍니다.
#include <iostream>
#include <compare>
struct Vector3D {
double x, y, z;
// spaceship operator를 사용하여 자동 비교 연산자 생성
auto operator<=>(const Vector3D&) const = default;
};
int main() {
Vector3D v1{1.0, 2.0, 3.0};
Vector3D v2{1.0, 2.0, 4.0};
std::cout << std::boolalpha;
std::cout << "v1 == v2: " << (v1 == v2) << "\n"; // == 연산자
std::cout << "v1 < v2: " << (v1 < v2) << "\n"; // < 연산자
std::cout << "v1 != v2: " << (v1 != v2) << "\n"; // != 연산자
}
출력 결과:
v1 == v2: false
v1 < v2: true
v1 != v2: true
Vector3D
구조체에서auto operator<=> = default;
를 사용하여==
,!=
,<
,>
,<=
,>=
연산자를 자동으로 생성합니다.Vector3D
객체를 비교할 때 각 구성 요소(x
,y
,z
)에 대해 비교가 이루어집니다.
예제 2: 정렬 가능한 학생 객체
학생 객체를 점수에 따라 정렬하는 예제입니다. spaceship operator를 사용하여 학생 객체를 쉽게 정렬할 수 있습니다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Student {
std::string name;
int score;
// 점수를 기준으로 비교 (score 값으로 우선 정렬)
auto operator<=>(const Student& other) const = default;
};
int main() {
std::vector<Student> students = {{"Alice", 90}, {"Bob", 85}, {"Charlie", 95}};
// 점수 기준으로 정렬
std::sort(students.begin(), students.end());
for (const auto& student : students) {
std::cout << student.name << ": " << student.score << "\n";
}
}
출력 결과:
Bob: 85
Alice: 90
Charlie: 95
Student
객체는score
값을 기준으로 자동으로 정렬됩니다.operator<=>
가 정의되어 있기 때문에 정렬 과정에서 비교 연산자가 자동으로 사용됩니다.
예제 3: 복잡한 객체 비교 (다중 기준)
두 사람을 나이와 이름을 기준으로 비교하는 예제입니다. 먼저 나이를 비교하고, 그 값이 같을 경우 이름을 기준으로 비교합니다.
#include <iostream>
#include <string>
#include <compare>
struct Person {
std::string name;
int age;
// 나이를 우선 비교하고, 나이가 같으면 이름으로 비교
auto operator<=>(const Person& other) const {
if (auto cmp = age <=> other.age; cmp != 0) return cmp;
return name <=> other.name;
}
};
int main() {
Person p1{"Alice", 30}, p2{"Bob", 25}, p3{"Alice", 25};
std::cout << std::boolalpha;
std::cout << "p1 == p2: " << (p1 == p2) << "\n";
std::cout << "p1 < p3: " << (p1 < p3) << "\n";
std::cout << "p2 > p3: " << (p2 > p3) << "\n";
}
출력 결과:
p1 == p2: false
p1 < p3: false
p2 > p3: true
Person
구조체에서는 나이가 우선 비교되고, 나이가 같으면 이름을 기준으로 비교됩니다.operator<=>
를 커스터마이즈하여 다중 기준의 비교를 효율적으로 처리할 수 있습니다.
실습 문제
이제 여러분이 직접 spaceship operator를 활용하여 문제를 해결해보세요. 아래 문제를 풀어보세요.
문제 1: 좌표 정렬하기
Point
구조체를 정의하고, x
, y
좌표를 기준으로 좌표 순으로 정렬하세요. x
좌표가 같으면 y
좌표를 기준으로 정렬하십시오.
#include <iostream>
#include <vector>
#include <algorithm>
#include <compare>
struct Point {
int x, y;
// spaceship operator를 사용하여 정렬 가능하게 만들기
auto operator<=>(const Point&) const = default;
};
int main() {
std::vector<Point> points = {{3, 4}, {1, 2}, {5, 1}, {2, 3}, {3, 2}};
// 정렬 코드 작성
std::sort(points.begin(), points.end());
// 정렬된 결과 출력
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ")\n";
}
}
문제 2: 직원 객체 정렬
Employee
구조체를 정의하고, 급여를 기준으로 내림차순 정렬하세요. 급여가 같으면 이름을 기준으로 오름차순 정렬해야 합니다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <compare>
struct Employee {
std::string name;
double salary;
// 급여 내림차순, 이름 오름차순 정렬을 위한 spaceship operator 작성
auto operator<=>(const Employee& other) const {
if (auto cmp = salary <=> other.salary; cmp != 0) return -cmp; // 급여 내림차순
return name <=> other.name; // 이름 오름차순
}
};
int main() {
std::vector<Employee> employees = {{"John", 50000}, {"Alice", 60000}, {"Bob", 50000}, {"Charlie", 70000}};
// 직원들 정렬 코드 작성
std::sort(employees.begin(), employees.end());
// 정렬된 결과 출력
for (const auto& emp : employees) {
std::cout << emp.name << ": " << emp.salary << "\n";
}
}
요약
- 예제를 통해 spaceship operator의 기본 사용법과 다양한 활용 방안을 살펴보았습니다.
- 실습 문제를 통해 직접 spaceship operator를 사용하여 객체 비교 및 정렬을 연습할 수 있습니다.
- 문제를 풀면서 다양한 기준으로 객체를 비교하고, 이를 통해 코드의 효율성을 높일 수 있습니다.
C++20의 spaceship operator는 객체 비교와 관련된 작업을 간소화하고, 코드의 가독성을 높이는 데 큰 도움이 됩니다.
요약
C++20의 spaceship operator(<=>
) 는 객체 비교 연산을 간결하게 처리하고 자동화하는 강력한 기능을 제공합니다. 기존 C++에서 비교 연산자를 일일이 오버로딩해야 했던 불편함을 해소하며, 코드 가독성과 유지보수성을 크게 향상시킵니다.
핵심 정리
- 비교 연산 자동 생성
auto operator<=> = default;
를 선언하면==, !=, <, >, <=, >=
연산자가 자동으로 생성됩니다.- 코드 중복이 줄어들고 유지보수가 간편해집니다.
- 강한/약한/부분 순서 비교 지원
std::strong_ordering
: 모든 비교가 완전한 순서를 가짐 (ex. 정수, 문자열).std::weak_ordering
: 내부적으로 다르지만 논리적으로 같은 경우 가능 (ex. 이름이 다른 동일 연령의 사람).std::partial_ordering
: 일부 값이 비교 불가능할 수도 있음 (ex. 부동소수점NaN
).
- 사용자 정의 클래스에서 비교 연산 간소화
operator<=>
를 사용하여 사용자 정의 객체를 쉽게 비교할 수 있음.- 다중 필드 비교가 가능하며, 원하는 기준을 설정할 수 있음.
- 표준 알고리즘(
std::sort
)과의 연동
std::sort()
와 같은 알고리즘이 spaceship operator를 자동으로 활용하여 객체를 정렬할 수 있음.- 사용자 지정 정렬 기준을 람다 함수 또는 비교 연산자 객체로 적용 가능.
- 실습을 통해 학습 강화
Point
,Employee
,Student
등의 예제를 통해 spaceship operator를 실제 코드에서 어떻게 활용하는지 배움.- 실습 문제를 통해 직접 적용하고 테스트할 수 있음.
결론
C++20의 spaceship operator는 비교 연산을 단순화하면서도 강력한 기능을 제공합니다.
이를 활용하면 객체 비교를 더욱 효율적으로 수행할 수 있으며, 코드의 유지보수성을 높이고 표준 라이브러리와 쉽게 연동할 수 있습니다.
C++을 활용한 소프트웨어 개발에서 spaceship operator를 적극적으로 사용하여 더 깔끔하고 직관적인 코드를 작성해보세요! 🚀