템플릿 특수화와 비타입 파라미터
템플릿 특수화
- 지난 시간에 함수 템플릿을 다루면서 아래 코드를 봤을 거에요
#include <iostream>
#include <string>
using namespace std;
template <typename T>
T getArraySum(const T arr[], int n) {
T sum = arr[0];
for (int i = 1; i < n; i++) {
sum += arr[i];
}
return sum;
}
int main() {
string sarr[3] = { "hello", "world", "luna" };
string ssum = getArraySum(sarr, 3);
cout << ssum << endl;
}
ssumd에서 hello, world, luna 사이에 공백을 넣어주고 싶다.
- 이는 일반적인 함수 템플릿으로는 불가능하다
템플릿 특수화를 통해서 템플릿 파라미터에 특정 타입이 들어왔을 때 다른 식으로 특수하게 작동하도록 만들어 줄 수 있다.
#include <iostream>
#include <string>
using namespace std;
template <typename T>
T getArraySum(const T arr[], int n) {
T sum = arr[0];
for (int i = 1; i < n; i++) {
sum += arr[i];
}
return sum;
}
// 템플릿 특수화
template<>
string getArraySum<string>(const string arr[], int n) {
string sum = arr[0];
for (int i = 1; i < n; i++) {
sum += ' ' + arr[i];
}
return sum;
}
int main() {
string sarr[3] = { "hello", "world", "luna" };
string ssum = getArraySum(sarr, 3);
cout << ssum << endl;
}
template 인자를 담는 꺽쇠 <> 사이에 아무것도 안적고, 아래에 함수 타입을 적어주면 해당 타입에 대해 특수화를 해주겠다는 의미이다.
특수화를 하게 되면, 특수화를 한 타입에 대해서는 특수화를 한 함수로 작동한다
int에 대해서 작동하도록 추가해서 직접 비교해보자
int main() {
string sarr[3] = { "hello", "world", "luna" };
string ssum = getArraySum(sarr, 3);
cout << ssum << endl;
int iarr[5] = { 3, 1, 4, 1, 5};
int isum = getArraySum(iarr, 5);
cout << isum << endl;
}
비타입 파라미터
c++에서 강력한 기능 중 하나
지금까지는 타입 파라미터만 사용해왔다.
- tempalte
- tempalte
타입이 아닌 일반적인 숫자 같은 것도 넣어줄 수 있다.
- template
- template
타입 파라미터와 비타입 파라미터를 섞어서 사용할 수도 있다
Example) Vector 클래스
#include <iostream>
using namespace std;
template <typename T>
class Vector {
public:
Vector(int n) {
this->n = n;
comp = new T[n];
}
~Vector() {
delete[] comp;
}
T GetComp(int i) const { // i 번째 성분을 리턴
return comp[i];
}
void SetComp(int i, T val) {
comp[i] = val;
}
Vector operator+(const Vector &rhs) const {
}
private:
int n;
T *comp; // 벡터의 성분
};
int main() {
Vector<float> v(3);
v.SetComp(0, 2);
v.SetComp(1, 3);
v.SetComp(2, 4);
cout << v.GetComp(0) << ", "<< v.GetComp(1) << ", " << v.GetComp(2) << endl;
// (1, 2) + (3, 4, 5)
}
(1, 2) + (3, 4, 5)와 같이 다른 차원의 덧셈을 할 때, operator+ 연산자 함수가 실행되지 않도록 일일히 사용자가 컨트롤해줘야 하는 불편함이 생긴다
이 때 비타입 파라미터로 차원을 받아보자
#include <iostream>
using namespace std;
template <typename T, int n>
class Vector {
public:
Vector() {
comp = new T[n];
}
~Vector() {
delete[] comp;
}
T GetComp(int i) const { // i 번째 성분을 리턴
return comp[i];
}
void SetComp(int i, T val) {
comp[i] = val;
}
Vector operator+(const Vector &rhs) const { // Vector<T, n>이 생략된 것
Vector res;
for (int i = 0; i < n; i++) {
res.SetComp(i, this->GetComp(i) + rhs.GetComp(i));
}
return res;
}
private:
T *comp; // 벡터의 성분
};
int main() {
Vector<float, 3> v1, v2;
Vector<float, 2> v4;
v1.SetComp(0, 2);
v1.SetComp(1, 3);
v1.SetComp(2, 4);
v2.SetComp(0, 5);
v2.SetComp(1, 6);
v2.SetComp(2, 7);
// 아래는 오류 발생. 타입이 다르기 때문
// Vector<float, 3> v3 = v1 + v4;
Vector<float, 3> v3 = v1 + v2;
cout << v3.GetComp(0) << ", "<< v3.GetComp(1) << ", " << v3.GetComp(2) << endl;
}
이렇게 해주면 this와 rhs의 차원이 같다는 것이 보장된다
Why?
+= 연산자에서 Vector는 Vector<T, n>이 생략된 것이다.
Vector<float, 2>와 Vector<float, 3>은 타입 자체가 다르기 때문에 다른 차원을 +=연산자의 매개변수로 받을 수 없게 된다
위 연산자에 문제점 하나가 있다.
임시 객체가 생기고 반환된다. 이 때 얕은 복사가 일어나 문제가 생긴다.
이전에 우리가 복사 대입 연산자와 복사 생성자를 통해서 이런 문제를 해결한 적이 있었다.
만들어 주기 전에 아래 내용을 먼저 살펴보자
template<typename T, int n>에서 n이라는 숫자는 타입의 일부이다.
- 그런데 타입의 일부를 클래스 안에서 그냥 사용할 수 있다
비타입 파라미터를 사용하지 않는 코드
template <typename T>
class Vector {
/* 생략 */
private:
/* 오류가 발생한다
int n;
T comp[n]; // 벡터의 성분
*/
};
n이 멤버 변수면 에러가 발생한다.
Why?
클래스가 정의되기 위해서는 클래스의 크기를 알아야 한다.
만약 클래스의 멤버중에 float comp[n]; 이라는게 있다면, n이 상황에 따라 달라질 수 있다. 따라서 배열의 크기를 클래스 안에 저장하는 것은 불가능하다. (동적인 시간에 동적할당을 사용하지 않고 배열의 크기를 정하는 것은 불가능)
만약 비타입 파라미터를 사용한다면?
#include <iostream>
using namespace std;
template <typename T, int n>
class Vector {
/* 생략 */
private:
T comp[n]; // 벡터의 성분
};
int main() {
Vector<float, 3> v1, v2;
Vector<float, 2> v4;
v1, v2에서 T에는 float가, n에는 3이 들어가서, Vector<float, 3> 타입이 된다 --> float comp[3] --> 12바이트
v4에서 T에는 float가, n에는 2이 들어가서, Vector<float, 2> 타입이 된다 --> float comp[2] --> 8바이트
comp 배열의 크기가 달라도, 애초에 타입의 크기가 다르기 때문에 문제가 없다
- 정적인 시간에 정해지기 때문에 동적할당에 대한 걱정도 할 필요가 없다
참조) 두들낙서 C/C++ 강좌
'Programming Language > C++' 카테고리의 다른 글
[C/C++] 함수 포인터 (0) | 2024.06.28 |
---|---|
[C++] 예외 처리(Exception handling) (0) | 2024.06.28 |
[C++] 함수 템플릿과 클래스 템플릿 (0) | 2024.06.28 |
[C++] 정사각형-직사각형 문제 (0) | 2024.06.28 |
[C++] 다중 상속과 다이아몬드 문제 (0) | 2024.06.28 |