C++ 13. Template

이도현·2023년 8월 9일
0

0. 템플릿이란

  • 함수나 클래스를 개별적으로 다시 작성하지 않아도, 여러 자료 형을 사용할 수 있도록 하게 만들어 놓은 틀
  • C++에선 함수 템플릿과 클래스 템플릿을 사용할 수 있다. 기본적인 사용방법과 템플릿 특수화에 대해서 알아보자

1. Function Template

  • 다양한 자료형에 대해 동일한 기능을 수행할 수 있는 함수를 작성할 수 있게 해준다.
template<typename T>
T getMAx(T x, T y)
{
	return (x > y) ? x : y;
}

int main()
{
	cout << getMax(1, 2) << endl;
	cout << getMax(3.14, 1.589) << endl;
	cout << getMax(1.0f, 3.4f) << endl;
	cout << getMax('a', 'c') << endl;

	return 0;
}

2. class template

  • 다양한 자료형에서 작동할 수 있는 범용 클래스를 정의할 수 있게 해줍니다. 예를 들어, 'std::vector'는 클래스 템플릿의 일종
  • 아래는 코드는 좀 길지만 한 번 읽으면서 어떻게 작동하는지 살펴보는 것이 좋다
// MyArray.h
#pragma once
#inlcude <assert.h> // for assert()
#inlcude <iostream>

template<typename T>
class My Array
{
private:
	int m_length;
	T *m_data;

public:
	MyArray()
	{
		m_length = 0;
		m_data = nullptr;
	}

	MyArray(int length)
	{
		m_data = new T [legnth];
		m_length = length;
	}
	~MyArray()
	{
		reset();
	}

	void reset()
	{
		delete[] = m_data;
		m_data = nullptr;
		m_length = 0;
	}

	T & operater[](int index)
	{
		assert(index >= 0 && index < m_length);
		return m_data[index]
	}

	int getLength()
	{
		return m_length;
	}
	
	void print()
	{
		for(int i = 0; i < m_length; ++i)
				cout << m_data[i] << " ";
		cout << endl;
	}
};
#inlcude "MyArray.h"

int main()
{
	MyArray my_array(10);

	for(int i = 0; i < my_array.getLength(); ++i)
		my_array[i] = i * 10;

	my_array.print();

	return 0;
}
// MyArray.cpp
#include "MyArray.h"

template<typenmase T>
void MyArray<T>::print()
{
	for(int i = 0; i < m_length; ++i)
			cout << m_data[i] << " ";
	cout << endl;
}

// explictit instantiation 링킹에러 해결
template void MyArray<char>::print();
template void MyArray<char>::print();
template class MyArray<char>;
template class MyArray<double>;

3. 자료형이 아닌 템플릿 매개변수

  • 템플릿 매개변수는 받느시 자료형일 필요 없으며, 정수나 포인터 같은 비자료형 값도 될 수 있다.
// MyArray.h
#pragma once
#inlcude <assert.h> // for assert()
#inlcude <iostream>

template<typename T, unsigned int T_SIZE> // T_SIZE의 compile time이 결정된다.
														//단, 이 경우 explicit instantitation하지 말기.
class My Array
{
private:
	int m_length;
	T *m_data;

public:
	MyArray()
	{
		//m_length = 0;
		m_data = nullptr;
	}

	.....
};

4. 템플릿 특수화

특수화란? 특정 자료형에 대해 함수 템플릿의 동작을 맞춤 설정하는 것. 특정 자료형에 최적화된 코드를 제공할 때 유용

1) 함수 템플릿 특수화

#include <iostream>

using namespace std;

template<typename T>
T getMax(T x, T y)
{
	reutrn (x > y) ? x : y;
}

//specialization
template<> // char가 value로 들어가면 이 친구가 사용된다.
char getMax(char x, char y)
{
	cout << "Warning : comparing chars" << endl;

	return(x > y) ? x : y;
}

int main()
{
	cout << getMax(1, 2) << endl;

	return 0;
}

2) 클래스 탬플릿 특수화

  • 결국은 다른 클래스를 하나 더 만드는 것이다.
#include <iostream>
#include <array>

using namespace std;

template<typename T>
class A
{
public:
	void doSomething()
	{
		cout << typeid(T).name() << endl;
	}

	void test()
	{}
};

template<>
class A<char>
{
public:
	void doSomthing();
	{
		cout << "char type specializatioin" << endl;
	}
};

int main()
{
	A<int> a_int;
	A<double> a_doulbe;
	A<char> a_char;

	//주의사항
	a_int.test();
	a.double.test();
	// a.char.test(); 실행되지 않는다. 생성자를 각 클래스에 넣어줄 경우 빌드 된다.

	a_int.doSomething();
	a_double.doSomething();
	a_char.doSomething();

	return 0;
}
	

5. 템플릿을 부분적으로 특수화하기

  • 클래스를 상속을받는 것이다. 클래스 템플릿의 이루 매개변수만 특수화하여, 템플릿의 일부만 다른 동작을 하도록하는 것
#include <iostream>
using namespace std;

template <class T, int size>
class StaticArray_Base
{
private:
	T m_array[size];

public:
	T * getArray() { return m_array; }

	T & operater[](int index)
	{
		return m_array[index];
	}

	void print()
	{
		for (int count = 0; count < size; ++count)
				std::cout << (*this)[count] << ' ';
		std::cout << endl;
	}
};

template <class T, int size> //상속 받고
class StaticArray : public StaticArray_Base<T, size>
{
};

template <class T, int size> //특수화하기
class StaticArray<char> : public StaticArray_Base<char, size>
{
public:
	void print()
		{
			for (int count = 0; count < size; ++count)
					std::cout << (*this)[count];
			std::cout << endl;
		}
};

7. 포인터에 대한 템플릿 특수화

  • 매개변수인 포인터를 특수화 함으로써, 포인터를 다루는 경우에 최적화된 코드를 제공
template<class T>
class A
{
private:
	T m_value;

public:
	A(const T & input)
			: m_value(input)
	{
	}

	void print()
	{
		cout << m_value << endl;
	}
};

template<class T>
class A<T*>
{
private:
	T* m_value;

public:
	A(const * input)
			: m_value(input)
	{
	}

	void print()
	{
		cout << *m_value << endl;
	}
};

int main()
{
	A<int> a_int(123);
	a.int.print();

	int temp = 456;

	A<int*> a_int_ptr(&temp);
	a_int_ptr.print();

	double temp_d = 3.141592;
	A<double*> a_double_ptr(&temp_d);
	a_double_ptr.print();

	return 0;
}

8. 멤버 함수를 한 번 더 템플릿화 하기

  • 이미 템플릿화된 클래스의 멤버 함수를 다시 템플릿화하여, 멤버 함수 수준에서 추가적인 유연성을 제공. 멤버 함수가 다양한 타입의 인자를 받아들일 수 있게 해준다.
template<class T>
class A
{
private:
	T m_value;

public:
	A(const T & input)
			: m_value(input)
	{
	}
	
	template<typename TT>
	void doSomething(const TT & input) // const TT & input이 중요
	{
		cout << typeid(T).name() << " " << typeid(TT).name() << endl;
	}
	void print()
	{
		cout << m_value << endl;
	}
};

int main()
{
	A<int> a_int(123);
	a.int.print();

	a_int.doSomehing(123.4);

	return 0;
}
profile
좋은 지식 나누어요

0개의 댓글