콘텐츠로 이동

C++ 연산자 오버로딩 설계

연산자 오버로딩은 사용자 정의 타입을 내장 타입처럼 자연스럽게 사용하게 해줍니다. 단, 의미론적 일관성을 유지하는 것이 핵심입니다. +가 덧셈이 아닌 동작을 하면 코드 가독성이 떨어집니다.

연산자권장 형태이유
=, [], (), ->멤버만 가능언어 규칙
+=, -=, *=멤버좌변 객체 수정
+, -, *비멤버(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++23
int& 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 반환 필수
  • 연산자의 직관적인 의미론 유지가 가장 중요