템플릿 특수화
템플릿 특수화의 개념
템플릿 특수화는 C++의 강력한 기능 중 하나로, 특정 타입이나 조건에 대해 템플릿의 다른 구현을 제공할 수 있게 해줍니다.
이를 통해 일반적인 알고리즘을 유지하면서도 특정 경우에 대한 최적화나 특별한 처리를 할 수 있습니다.
템플릿 특수화의 주요 목적
- 특정 타입에 대한 최적화
- 특별한 동작이 필요한 타입 처리
- 컴파일 타임 최적화
함수 템플릿 특수화
일반 함수 템플릿
먼저, 일반적인 함수 템플릿을 살펴봅시다.
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
이 템플릿은 대부분의 타입에 대해 잘 작동하지만, 문자열(C-style string)과 같은 특정 타입에 대해서는 적절하지 않을 수 있습니다.
특수화된 함수 템플릿
문자열을 위한 특수화된 버전을 만들어 봅시다.
template <>
const char* max<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b;
}
이제 max
함수는 문자열에 대해 올바르게 동작합니다.
int main() {
std::cout << max(10, 20) << std::endl; // 출력: 20
std::cout << max("apple", "banana") << std::endl; // 출력: banana
return 0;
}
클래스 템플릿 특수화
일반 클래스 템플릿
다음과 같은 일반 클래스 템플릿이 있다고 가정해봅시다.
template <typename T>
class Container {
private:
T element;
public:
void set(T arg) { element = arg; }
T get() const { return element; }
};
완전 특수화
bool
타입에 대해 메모리를 절약하는 특수화를 구현할 수 있습니다.
template <>
class Container<bool> {
private:
unsigned char element : 1;
public:
void set(bool arg) { element = arg; }
bool get() const { return element; }
};
이 특수화는 bool
값을 저장할 때 1비트만 사용합니다.
부분 특수화
부분 특수화는 템플릿 매개변수의 일부만 특수화하는 것을 말합니다. 클래스 템플릿에서만 가능합니다.
// 일반 템플릿
template <typename T, typename U>
class MyClass {
public:
void print() { std::cout << "General template" << std::endl; }
};
// T가 int인 경우에 대한 부분 특수화
template <typename U>
class MyClass<int, U> {
public:
void print() { std::cout << "Partial specialization for int" << std::endl; }
};
// 포인터 타입에 대한 부분 특수화
template <typename T, typename U>
class MyClass<T*, U*> {
public:
void print() { std::cout << "Partial specialization for pointers" << std::endl; }
};
int main() {
MyClass<double, float> obj1;
MyClass<int, float> obj2;
MyClass<int*, float*> obj3;
obj1.print(); // 출력: General template
obj2.print(); // 출력: Partial specialization for int
obj3.print(); // 출력: Partial specialization for pointers
return 0;
}
템플릿 특수화의 장단점
장점
- 타입별 최적화 가능
- 특정 타입에 대한 특별한 동작 구현 가능
- 컴파일 타임 최적화
단점
- 코드 복잡성 증가
- 유지보수의 어려움
- 과도한 사용 시 코드 중복 가능성
실습 : 스마트 포인터 템플릿
다음 요구사항을 만족하는 간단한 스마트 포인터 템플릿을 구현해봅시다.
SmartPtr
클래스 템플릿 작성- 일반 타입과 배열 타입에 대한 특수화 구현
- 참조 카운팅 기능 추가
#include <iostream>
// 일반 템플릿
template <typename T>
class SmartPtr {
private:
T* ptr;
int* refCount;
public:
SmartPtr(T* p = nullptr) : ptr(p), refCount(new int(1)) {
std::cout << "Creating SmartPtr (general)" << std::endl;
}
SmartPtr(const SmartPtr& other) : ptr(other.ptr), refCount(other.refCount) {
(*refCount)++;
std::cout << "Copying SmartPtr (general)" << std::endl;
}
~SmartPtr() {
std::cout << "Destroying SmartPtr (general)" << std::endl;
if (--(*refCount) == 0) {
delete ptr;
delete refCount;
}
}
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
SmartPtr& operator=(const SmartPtr& other) {
if (this != &other) {
if (--(*refCount) == 0) {
delete ptr;
delete refCount;
}
ptr = other.ptr;
refCount = other.refCount;
(*refCount)++;
}
return *this;
}
};
// 배열에 대한 부분 특수화
template <typename T>
class SmartPtr<T[]> {
private:
T* ptr;
int* refCount;
public:
SmartPtr(T* p = nullptr) : ptr(p), refCount(new int(1)) {
std::cout << "Creating SmartPtr (array)" << std::endl;
}
SmartPtr(const SmartPtr& other) : ptr(other.ptr), refCount(other.refCount) {
(*refCount)++;
std::cout << "Copying SmartPtr (array)" << std::endl;
}
~SmartPtr() {
std::cout << "Destroying SmartPtr (array)" << std::endl;
if (--(*refCount) == 0) {
delete[] ptr;
delete refCount;
}
}
T& operator[](int index) { return ptr[index]; }
SmartPtr& operator=(const SmartPtr& other) {
if (this != &other) {
if (--(*refCount) == 0) {
delete[] ptr;
delete refCount;
}
ptr = other.ptr;
refCount = other.refCount;
(*refCount)++;
}
return *this;
}
};
int main() {
SmartPtr<int> sp1(new int(5));
std::cout << "*sp1 = " << *sp1 << std::endl;
SmartPtr<int[]> sp2(new int[3]{1, 2, 3});
std::cout << "sp2[1] = " << sp2[1] << std::endl;
return 0;
}
템플릿 특수화와 오버로딩의 차이
템플릿 특수화와 함수 오버로딩은 다른 개념입니다.
- 특수화 : 기존 템플릿의 특정 인스턴스에 대한 다른 구현 제공
- 오버로딩 : 같은 이름의 다른 함수 정의
// 예제
#include <iostream>
#include <cstring>
// 일반 템플릿
template <typename T>
void print(T value) {
std::cout << "Template: " << value << std::endl;
}
// const char* 에 대한 특수화
template <>
void print<const char*>(const char* value) {
std::cout << "Specialization for const char*: " << value << std::endl;
}
// 오버로딩
void print(int value) {
std::cout << "Overload for int: " << value << std::endl;
}
int main() {
print(5.5); // 일반 템플릿 사용
print("Hello"); // const char* 특수화 사용
print(10); // 오버로딩된 함수 사용
return 0;
}
연습 문제
Array
클래스 템플릿을 구현하고,bool
타입에 대해 특수화하여 각 요소를 1비트로 저장하도록 만드세요.max
함수 템플릿을 구현하고, C-style 문자열에 대한 특수화를 작성하세요.
참고자료
- "C++ Templates : The Complete Guide" by David Vandevoorde and Nicolai M. Josuttis
- "Modern C++ Design" by Andrei Alexandrescu
- "Effective C++" by Scott Meyers (항목 25 : 타입 추론 규칙을 숙지하라)
- C++ Reference : Template specialization
- C++ Core Guidelines : T.60: Use template specialization to provide alternative implementations of class templates