템플릿(Template)과 연산자 오버로딩
일반화 프로그래밍(Generic Programming)
•
객체 지향: 데이터 중심
•
일반화 프로그래밍: 알고리즘 중심
•
목표: 타입마다 별도 함수/클래스를 만들지 않고 범용적으로 사용
템플릿(Template)이란?
•
*매개변수화 타입(parameterized type)**이라고도 불림
•
컴파일 시점에 실제 타입으로 대체됨
변수 템플릿
// variable template
template <typename T>
constexpr T pi = T(3.1415926535897932385L);
int main()
{
float f = pi<float>; // float 버전의 pi
double d = pi<double>; // double 버전의 pi
int i = pi<int>; // int 버전의 pi (3)
return 0;
}
C++
복사
함수 템플릿
기본 문법
template <typename T>
반환타입 함수이름(T 매개변수)
{
// 함수 본체
}
C++
복사
실제 사용 예시
// ❌ 기존 방식: 타입마다 별도 함수 필요
//int Add(int a, int b) { return a + b; }
//float Add(float a, float b) { return a + b; }
// ✅ 템플릿 방식: 하나의 함수로 모든 타입 처리
template <typename T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
int a = Add<int>(1, 2); // int 버전 호출
float b = Add<float>(1.0f, 2.0f); // float 버전 호출
return 0;
}
C++
복사
Add<int>(1, 2) 호출 시:
├─ 컴파일러가 int 버전의 Add 함수 자동 생성
└─ int Add(int a, int b) { return a + b; }
Plain Text
복사
명시적 특수화(Explicit Specialization)
template <typename T>
void Swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
// double 타입에 대한 특별한 처리
template <>
void Swap<double>(double& a, double& b)
{
// double형은 값을 서로 바꾸지 않음
std::cout << "double은 교환하지 않습니다!" << std::endl;
}
C++
복사
클래스 템플릿
template <typename T>
class Data
{
public:
T GetData() { return mData; }
void SetData(T data) { mData = data; }
private:
T mData; // T 타입의 데이터 저장
};
int main()
{
Data<int> intData; // int용 Data 클래스
Data<float> floatData; // float용 Data 클래스
intData.SetData(100);
floatData.SetData(3.14f);
return 0;
}
C++
복사
가변인자 템플릿 (C++11)
기본 문법
template <typename... Args> // Args: 타입들의 집합
void function_name(Args... args) // args: 실제 값들의 집합
{
// 함수 본체
}
C++
복사
재귀적 처리 방식
#include <iostream>
#include <print>
#include <vector>
// 재귀 종료 조건
void print()
{
std::cout << std::endl;
}
// 가변인자 템플릿 함수
template <typename T, typename... Args>
void print(T first, Args... args)
{
std::cout << first << " ";
print(args...); // 나머지 인자로 재귀 호출
}
int main()
{
print(1, "Hello", 3.14, 'A'); // "1 Hello 3.14 A" 출력
return 0;
}
C++
복사
C++17 Fold Expressions
#include <iostream>
#include <print>
#include <vector>
template <typename... Args>
auto sum(Args... args)
{
// print args...
return (... + args); // 모든 인자를 더함
}
template <typename... Args>
void printAll(Args... args)
{
(std::print("{} ", args), ...); // 모든 인자를 출력
std::print("\n");
}
int main()
{
int result = sum(1, 2, 3, 4, 5); // 15 반환
printAll(1, 2, 3, 4, 5); // "1 2 3 4 5" 출력
return 0;
}
C++
복사
템플릿으로 연결리스트 만들기
#include <iostream>
#include <print>
#include <vector>
#include <list>
namespace ya
{
template <typename T>
class list
{
public:
struct Node
{
T data; // 제네릭 데이터
Node* back; // 다음 노드 포인터
};
list() // 생성자
{
mHead = nullptr;
mTail = nullptr;
}
~list() // 소멸자
{
// 메모리 해제 코드 필요
}
void push_back(T data)
{
if (mHead == nullptr) // 첫 번째 노드
{
mHead = new Node();
mHead->data = data;
mHead->back = nullptr;
mTail = mHead;
}
else // 기존 노드가 있는 경우
{
mTail->back = new Node();
mTail->back->data = data;
mTail->back->back = nullptr;
mTail = mTail->back;
}
}
private:
Node* mHead; // 첫 번째 노드
Node* mTail; // 마지막 노드
};
}
int main()
{
ya::list<int> intList; // int용 리스트
intList.push_back(1);
intList.push_back(2);
std::list<int> stdList; // std::list 사용 예시
stdList.push_back(1);
stdList.push_back(2);
return 0;
}
C++
복사
연산자 오버로딩(Operator Overloading)
기본 문법
타입 operator연산자기호(매개변수)
{
// 연산자 동작 정의
}
C++
복사
오버로딩 가능한 연산자들
카테고리 | 연산자들 |
산술 | +, -, *, /, % |
대입 | =, +=, -=, *=, /= |
비교 | ==, !=, <, >, <=, >= |
논리 | &&, ` |
기타 | [], (), ->, ++, -- |
오버로딩 불가능한 연산자들
•
. (멤버 선택)
•
:: (범위 지정)
•
?: (삼항 연산자)
•
sizeof
실제 구현 예시
Point 구조체 연산자 오버로딩
struct Point
{
int x, y;
// ➕ 덧셈 연산자
Point operator+(Point other)
{
Point result;
result.x = x + other.x;
result.y = y + other.y;
return result;
}
// ➖ 뺄셈 연산자 (Point - Point)
Point operator-(Point other)
{
Point result;
result.x = x - other.x;
result.y = y - other.y;
return result;
}
// ➖ 뺄셈 연산자 (Point - int)
Point operator-(int value)
{
Point result;
result.x = x - value;
result.y = y - value;
return result;
}
// 🔍 비교 연산자
bool operator<(Point other)
{
return (x < other.x && y < other.y);
}
};
int main()
{
Point p1 = {1, 1};
Point p2 = {3, 2};
Point p3 = p1 + p2; // p1.operator+(p2) 호출
Point p4 = p1 - p2; // p1.operator-(p2) 호출
Point p5 = p1 - 5; // p1.operator-(5) 호출
if (p1 < p2) { // p1.operator<(p2) 호출
std::cout << "p1이 p2보다 작습니다" << std::endl;
}
return 0;
}
C++
복사
Complex 수 연산자 오버로딩
struct Complex
{
double re, im; // 실수부, 허수부
Complex(double r, double i) : re(r), im(i) {}
void Display() {
std::cout << re << " + " << im << "i" << std::endl;
}
// 복소수 덧셈
Complex operator+(Complex& other)
{
return Complex(re + other.re, im + other.im);
}
};
int main()
{
Complex a(1.2, 3.4); // 1.2 + 3.4i
Complex b(5.6, 7.8); // 5.6 + 7.8i
Complex c = a + b; // (6.8, 11.2)
c.Display(); // "6.8 + 11.2i" 출력
return 0;
}
C++
복사
숙제 안내
ya::list 구조 분석
•
ya::list를 3번 따라 치며 디버깅
•
메모리 구조 그림 그리기
ya::list 기능 확장
yaList.push_front(10); // 앞쪽에 추가하는 함수
int len = yaList.size(); // 크기 반환하는 함수
C++
복사
Iterator 구현 (고급)
for (ya::list<int>::iterator iter = yaList.begin();
iter != yaList.end(); ++iter)
{
std::cout << *iter << std::endl;
}
C++
복사
Vector2 연산자 오버로딩
struct Vector2
{
double x, y;
explicit Vector2(double x_, double y_) : x(x_), y(y_) {}
// 10가지 이상의 연산자 구현하기
// ==, !=, +, -, *, /, +=, -=, *=, /=, <, >, <= 등
};
C++
복사
•
템플릿: 하나의 코드로 여러 타입 지원
•
연산자 오버로딩: 사용자 정의 타입에 직관적인 연산 제공
•
제네릭 프로그래밍: 재사용성과 타입 안전성 확보