템플릿(Template) 프로그래밍 2
FixedVector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
#pragma once
template<typename T, size_t N>
class FixedVector
{
public:
FixedVector();
bool Add(const T& data);
size_t GetSize() const;
size_t GetCapacity() const;
private:
size_t mSize;
T mArray[N];
};
template<typename T, size_t N>
FixedVector<T, N>::FixedVector()
: mSize(0), mArray()
{
}
template<typename T, size_t N>
size_t FixedVector<T, N>::GetSize() const
{
return mSize;
}
template<typename T, size_t N>
size_t FixedVector<T, N>::GetCapacity() const
{
return N;
}
template<typename T, size_t N>
bool FixedVector<T, N>::Add(const T& data)
{
if (mSize >= N)
{
return false;
}
mArray[mSize++] = data;
return true;
}
|
cs |
MyPair
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#pragma once
template<typename T, typename U>
class MyFair
{
public:
const T& GetFirst() const;
const U& GetSecond() const;
MyFair(const T& first, const U& second);
private:
T mFirst;
U mSecond;
};
template<typename T, typename U>
const T& MyFair<T, U>::GetFirst() const
{
return mFirst;
}
template<typename T, typename U>
const U& MyFair<T, U>::GetSecond() const
{
return mSecond;
}
template<typename T, typename U>
MyFair<T, U>::MyFair(const T& frist, const U& second)
: mFirst(first), mSecond(second)
{
}
|
cs |
Math
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#pragma once
namespace Math
{
template<typename T>
static T Add(T value1, T value2)
{
return value1 + value2;
}
template<typename T>
static T Divide(T value1, T value2)
{
return value1 / value2;
}
template<typename T>
static T Multiply(T value1, T value2)
{
return value1 * value2;
}
template<typename T>
static T Max(T value1, T value2)
{
return value1 < value2 ? value2 : value1;
}
template<typename T>
static T Min(T value1, T value2)
{
return value1 < value2 ? value1 : value2;
}
}
|
cs |
템플릿 특수화(Specialization)
특정한 템플릿 매개변수를 받도록 템플릿 코드를 커스터마이즈 할 수 있음
제네릭 템플릿이 어느 형에나 적용됨
단, 특정 형에 특수화된 템플릿이 있다면 그 특수화된 템플릿이 적용됨
그렇게 자주 쓰이지는 않음
> std::vector에 좋은 예
template <class T, class Allocator>
class std::vector<T, Allocator> {} // 모든 형을 받는 제네릭 vector
template <class Allocator>
class std::vector<bool, Allocator> {} // bool을 받도록 특수화된 vector
bool은 왜 다를까?
> 하나의 비트로도 표현할 수 있지만, bool은 1바이트, BOOL은 4바이트를 사용
> 메모리 공간을 조금 더 효과적으로 사용하기 위해 템플릿 특수화를 했다고 볼 수 있음
전체 템플릿 특수화
> 템플릿 매개변수 리스트가 비어 있음
template<typename VAL, typename EXP>
VAL Power(const VAL value, EXP exponent) () // 모든 형을 받는 제네릭 Power()
template<>
float Power(float value, float exp) {} // float을 받도록 특수화된 Power()
부분 템플릿 특수화
template<class T, class Allocator>
class std::vector(T, Allocator> {} // 모든 형을 받는 제네릭 vector
template<class Allocator>
class std::vector(bool, Allocator) {} // bool을 받도록 특수화된 vector
템플릿 프로그래밍의 장점과 단점
컴파일러가 컴파일 도중에 각 템플릿 인스턴스에 대한 코드를 만들어 줌
> 컴파일 타임은 비교적 느림
> 템플릿 매개변수를 추가할수록 더 느려짐
> 하지만 런타임 속도는 더 빠를지도 모름
> 실행 파일 크기가 커지기 때문에 항상 그런 것은 아님 (캐시 효용성이 떨어져서)
> C#과 Java도 어느 정도 해당되는 말 (그래서 ArrayList를 쓰지 말라는 것)
자료형만 다른 중복 코드를 없애는 훌륭한 방법
하지만 쓸모없는 템플릿 변형을 막을 방법이 없음
> Add<int>, Add<short>, Add<unsigned int>
> Vector<3>, Vector<4>, Vector<15> (템플릿 매개변수는 요소의 수)
> 최대한 제네릭 함수를 짧게 유지할 것 (코드의 크기가 커지므로)
> 제네릭이 아니어도 되는 부분은 별도의 함수로 옮기는 것도 방법. 이 함수가 인라인 될 수도 있음.
컴파일 도중에 다형성을 부여할 수 있음
> C++ 프로그래머가 너무 많이 잘못 썼던 기능
> 가상 테이블이 없어서 프로그램이 더 빠름
> 하지만 exe 파일이 커지면 느려질 수 있음
코드 읽기가 더 힘듦
디버깅이 좀 이상할 수 있음
> 더 이상 큰 문제는 아니지만...
템플릿 프로그래밍 베스트 프랙티스
컨테이너의 경우 매우 적합
> 아주 다양한 형들을 저장할 수 있음
> 그런 이유로 Java와 C# 제네릭이 주로 컨테이너에 쓰이는 것
컨테이너가 아닌 경우
> 각기 다른 서넛 이상의 자료형을 다룬다면 템플릿을 쓰자
> 두 가지 정도라면 그냥 클래스를 2개 만들자
class Math;
class FloatMath;
출처 : 포큐아카데미 C++ 언매니지드 프로그래밍