C++ 연산자 오버로딩 설계
기본 원칙
섹션 제목: “기본 원칙”연산자 오버로딩은 사용자 정의 타입을 내장 타입처럼 자연스럽게 사용하게 해줍니다. 단, 의미론적 일관성을 유지하는 것이 핵심입니다. +가 덧셈이 아닌 동작을 하면 코드 가독성이 떨어집니다.
멤버 vs 비멤버 선택 기준
섹션 제목: “멤버 vs 비멤버 선택 기준”| 연산자 | 권장 형태 | 이유 |
|---|---|---|
=, [], (), -> | 멤버만 가능 | 언어 규칙 |
+=, -=, *= | 멤버 | 좌변 객체 수정 |
+, -, * | 비멤버(friend) | 좌변 암묵적 변환 지원 |
<<, >> (스트림) | 비멤버(friend) | 좌변이 ostream/istream |
==, !=, < | 비멤버(friend) | 양쪽 대칭 변환 지원 |
class Vec2 {public: float x, y;
// 멤버: 복합 대입 Vec2& operator+=(const Vec2& rhs) { x += rhs.x; y += rhs.y; return *this; }
// 비멤버: 이항 연산 (+=를 재활용) friend Vec2 operator+(Vec2 lhs, const Vec2& rhs) { return lhs += rhs; // lhs는 값 복사 }
// 비멤버: 스트림 출력 friend std::ostream& operator<<(std::ostream& os, const Vec2& v) { return os << '(' << v.x << ", " << v.y << ')'; }};비교 연산자와 C++20 우주선 연산자
섹션 제목: “비교 연산자와 C++20 우주선 연산자”C++20 이전에는 6개의 비교 연산자를 모두 구현해야 했습니다.
// C++17 이하bool operator==(const Vec2& rhs) const { return x == rhs.x && y == rhs.y; }bool operator!=(const Vec2& rhs) const { return !(*this == rhs); }bool operator<(const Vec2& rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); }// ... 나머지 3개도 필요C++20에서는 <=> (우주선 연산자) 하나로 모든 비교가 자동 생성됩니다.
#include <compare>
class Vec2 {public: float x, y; auto operator<=>(const Vec2&) const = default; // 모든 비교 자동 생성 bool operator==(const Vec2&) const = default; // == 도 명시 권장};
Vec2 a{1, 2}, b{1, 3};bool less = a < b; // 자동 생성된 <반환 타입(std::strong_ordering, std::weak_ordering, std::partial_ordering)은 비교 의미론에 따라 다릅니다. float 멤버는 partial_ordering(NaN 때문)이 됩니다.
[] 연산자
섹션 제목: “[] 연산자”class Buffer { std::vector<int> data_;public: int& operator[](size_t i) { return data_[i]; } const int& operator[](size_t i) const { return data_[i]; }};C++23에서는 다차원 인덱스를 지원합니다.
// C++23int& operator[](size_t r, size_t c) { return data_[r * cols_ + c]; }mat[1, 2] = 5;함수 호출 연산자 ()
섹션 제목: “함수 호출 연산자 ()”함수 객체(functor)를 만들 때 사용합니다.
struct Multiplier { int factor; int operator()(int x) const { return x * factor; }};
Multiplier times3{3};int result = times3(5); // 15람다는 내부적으로 이 방식으로 구현됩니다.
흔한 실수
섹션 제목: “흔한 실수”// 대입 연산자: 자기 대입 체크 + *this 반환Vec2& operator=(const Vec2& rhs) { if (this == &rhs) return *this; x = rhs.x; y = rhs.y; return *this; // 체이닝 지원}
// 증감 연산자: 전위/후위 구분Vec2& operator++() { ++x; return *this; } // 전위Vec2 operator++(int) { Vec2 tmp = *this; ++x; return tmp; } // 후위+=등 복합 대입은 멤버로,+등 이항 연산은 비멤버(friend)로 구현- C++20
<=>하나로 6개 비교 연산자 자동 생성 - 대입 연산자는 자기 대입 보호와
*this반환 필수 - 연산자의 직관적인 의미론 유지가 가장 중요