profile image

L o a d i n g . . .

반응형

함수 템플릿

프로그래머의 단순 반복 작업을 줄여 주기 위한 장치가 있다. 템플릿은 여러 가지 자료형에 대해서 비슷한 코드를 반복하는 것을 방지해 준다.

Cents.h

#pragma once
#include <iostream>

class Cents
{
private:
   int m_cents;

public:
   Cents(int cents)
      : m_cents(cents)
   {
   }

   friend bool operator > (const Cents &c1, const Cents &c2)
   {
      return (c1.m_cents > c2.m_cents);
   }

   friend std::ostream& operator << (std::ostream &out, const Cents &cents)
   {
      out << cents.m_cents << " cents ";
      return out;
   }
};

main.cpp

#include <iostream>
#include "Cents.h"

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


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

   // user defined type에 대해서도 템플릿 사용 가능
   // 단, 이 경우 << operator, < operator 등이 구현되어 있어야 사용 가능함 주의!
   // template instantiation
   std::cout << getMax(Cents(5), Cents(9)) << std::endl;

   return 0;
}

 

 

클래스 템플릿

MyArray.h

#pragma once
#include <assert.h>	// for assert()
#include <iostream>

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

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

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

	~MyArray()
	{
		reset();
	}

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

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

	int getLength()
	{
		return m_length;
	}

	void print();
};

//template<typename T>
//inline void MyArray<T>::print()	// MyArray::print()가 아니라 MyArray"<T>"!!
//{
//	for (int i = 0; i < m_length; ++i)
//		std::cout << m_data[i] << " ";
//	std::cout << std::endl;
//}

MyArray.cpp

#include "MyArray.h"

// cpp 파일로 옮기면 linking error!!
// => 정의는 cpp 파일에 있고, main.cpp는 MyArray.h를 include함
//    따라서 instantiation을 할 때, 어떤 타입으로 해야 하는지를 print()를 컴파일할 때 알 수 없음
// MyArray.cpp를 include하면 해결이 되나, 프로젝트가 커지면 오류 발생 가능성 높음

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

// 해결 방법: explicit instantiation
//template void MyArray<double>::print();
//template void MyArray<char>::print();	// 내가 이 print라는 함수를 char 타입으로 사용할 거니까 빌드할 때 instantiation 하라

// 클래스 자체를 exlicit
template class MyArray<double>;
template class MyArray<char>;

main.cpp

#include "MyArray.h"

int main()
{
	MyArray<double> my_array(10);

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

	MyArray<char> my_array_c(10);

	for (int i = 0; i < my_array_c.getLength(); ++i)
		my_array_c[i] = i + 65;

	my_array.print();
	my_array_c.print();

	return 0;
}

 

 

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

#pragma once
#include <assert.h>	// for assert()
#include <iostream>

template<typename T, unsigned int T_SIZE>
class MyArray
{
private:
	//int m_length;
	T *m_data;	// T m_data[T_SIZE]

public:
	MyArray()
	{
		m_data = new T[T_SIZE];
	}

	~MyArray()
	{
		delete[] m_data;
	}

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

	int getLength()
	{
		return T_SIZE;
	}

	// explicit instantiation을 한다면, T_SIZE로 들어오는 모든 경우에 대해 다 처리해야 함 => ??
	// non-type의 경우 헤더에서 사용하는 것이 괜찮은 듯하다!
	void print()
	{
		for (int i = 0; i < T_SIZE; ++i)
			std::cout << m_data[i] << " ";
		std::cout << std::endl;
	}
};
#include "MyArray.h"

int main()
{
	// std::vector<double> my_array; my_array.resize(100);
	// MyArray<double> my_array(100); => 이전 예제는 동적 할당이었기 때문에 cin 등으로도 처리 가능했음
	
	int i = 100;
	//MyArray<double, i> my_array;	// error! int i에 const 붙이면 OK
	MyArray<double, 100> my_array;	// std::array<double,100>

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

	my_array.print();
}

 

 

함수 템플릿 특수화

#include <iostream>

using namespace std;

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

// 특정 타입의 경우 다르게 작동하게끔 구현
template<>
char getMax(char x, char y)
{
	cout << "Warning: comparing chars" << endl;
	return (x > y) ? x : y;
}

int main()
{
	cout << getMax(1, 2) << endl;	// == getMax<int>(1, 2)
	cout << getMax<double>(1, 2) << endl;
	cout << getMax('a', 'b') << endl;

	return 0;
}

Storage.h

#pragma once
#include <iostream>

template <class T>
class Storage
{
private:
	T m_value;

public:
	Storage(T value)
	{
		m_value = value;
	}

	~Storage()
	{}

	void print()
	{
		std::cout << m_value << '\n';
	}
};

// 헤더파일에서 구현하는 것이 가장 좋은 방법!
// cpp에서 사용하고 싶다면 main.cpp에 #include "Storage.cpp"
template <>
void Storage<double>::print()
{
	std::cout << "Double Type ";
	std::cout << std::scientific << m_value << '\n';
}
#include <iostream>
#include "Storage.h"

using namespace std;

int main()
{
	Storage<int> nValue(5);
	Storage<double> dValue(6.7);

	nValue.print();
	dValue.print();

	return 0;
}

 

 

클래스 템플릿 특수화

#include <iostream>
#include <array>
#include "Storage8.h"

using namespace std;

template<typename T>
class A
{
public:
	A(const T& input)
	{
	}

	void doSomething()
	{
		cout << typeid(T).name() << endl;
	}

	void test()
	{}
};

template<>
class A<char>
{
public:
	A(const char & temp)
	{}

	void doSomething()
	{
		cout << "Char type specialization" << endl;
	}
};

int main()
{
	// C++17에서는 컴파일 가능!
	//A a_int(1);
	A<int> a_int(1);
	A<double> a_double(3.14);
	A<char> a_char('A');

	// 주의사항: 다른 클래스라고 보면 됨!!
	a_int.test();
	a_double.test();
	//a_char.test();	// error! 없음

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

	return 0;
}

 

Storage8.h

#pragma once
template <class T>
class Storage8
{
private:
	T m_array[8];

public:
	void set(int index, const T &value)
	{
		m_array[index] = value;
	}

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

main.cpp

#include <iostream>
#include "Storage8.h"

using namespace std;

int main()
{
	Storage8<int> intStorage;

	for (int count = 0; count < 8; ++count)
		intStorage.set(count, count);
	for (int count = 0; count < 8; ++count)
		std::cout << intStorage.get(count) << '\n';

	cout << "Sizeof Storage8<int> " << sizeof(Storage8<int>) << endl;

	// Define a Storage8 for bool
	Storage8<bool> boolStorage;
	for (int count = 0; count < 8; ++count)
		boolStorage.set(count, count & 3);

	for (int count = 0; count < 8; ++count)
		std::cout << (boolStorage.get(count) ? "true" : "false") << '\n';

	cout << "Sizeof Storage8<bool> " << sizeof(Storage8<bool>) << endl;

	return 0;
}

 

Storage8.h

#pragma once
template <class T>
class Storage8
{
private:
	T m_array[8];

public:
	void set(int index, const T &value)
	{
		m_array[index] = value;
	}

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


template<>
class Storage8<bool>
{
private:
	unsigned char m_data;	// 1 byte

public:
	Storage8() : m_data(0)
	{
	}

	void set(int index, bool value)
	{
		unsigned char mask = 1 << index;

		if (value)
			m_data |= mask;
		else
			m_data &= ~mask;
	}

	bool get(int index)
	{
		unsigned char mask = 1 << index;
		return (m_data & mask) != 0;
	}
};

 

 

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

#include <iostream>
using namespace std;

template <class T, int size>
class StaticArray
{
private:
	T my_array[size];

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

	T& operator[](int index)
	{
		return my_array[index];
	}
};

template <typename T, int size>
void print(StaticArray<T, size> & array)
{
	for (int count = 0; count < size; ++count)
		std::cout << array[count] << ' ';
	std::cout << std::endl;
}
int main()
{
	StaticArray<int, 4> int4;
	int4[0] = 1;
	int4[1] = 2;
	int4[2] = 3;
	int4[3] = 4;

	print(int4);

	StaticArray<char, 14> char14;
	char14[0] = 'H';
	char14[1] = 'e';
	// ...
	strcpy_s(char14.getArray(), 14, "Hello, World");

	print(char14);

	return 0;
}

//...

template <typename T, int size>
void print(StaticArray<T, size> & array)
{
	for (int count = 0; count < size; ++count)
		std::cout << array[count] << ' ';
	std::cout << std::endl;
}

// 부분적으로 특수화하기, char 타입인 경우에만
template <int size>
void print(StaticArray<char, size> & array)
{
	for (int count = 0; count < size; ++count)
		std::cout << array[count];
	std::cout << std::endl;
}

 

#include <iostream>
using namespace std;

template <class T, int size>
class StaticArray_BASE
{
private:
	T my_array[size];

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

	T& operator[](int index)
	{
		return my_array[index];
	}

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

// 상속으로 처리하는 이유? 템플릿을 통째로 특수화하는 것보다 편리함
template <class T, int size>
class StaticArray : public StaticArray_BASE<T, size>
{
};

template <int size>
class StaticArray<char, size> : public StaticArray_BASE<char, size>
{
public:
	void print()
	{
		for (int count = 0; count < size; ++count)
			std::cout << (*this)[count];
		std::cout << std::endl;
	}
};


int main()
{
	StaticArray<int, 4> int4;
	int4[0] = 1;
	int4[1] = 2;
	int4[2] = 3;
	int4[3] = 4;

	int4.print();

	StaticArray<char, 14> char14;
	char14[0] = 'H';
	char14[1] = 'e';
	// ...
	strcpy_s(char14.getArray(), 14, "Hello, World");

	char14.print();

	return 0;
}

 

 

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

#include <iostream>
using namespace std;

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

public:
	A(const T & 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;
}

 

#include <iostream>
using namespace std;

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(T * 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;
}

 

 

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

#include <iostream>
using namespace std;

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

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

	// TT: doSomething()에서만 적용되는 template parameter
	template<typename TT>
	void doSomething(const TT & input)
	{
		cout << typeid(T).name() << " " << typeid(TT).name() << endl;
		cout << (TT)m_value << endl;
	}

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

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

	// <float>: TT가 어떤 타입인지 명시, 매개변수를 넣는 경우 생략 가능
	a_int.doSomething<float>(123.4);	//truncation from 'double' to 'const TT'
	a_int.doSomething(123.4);

	A<char> a_char('A');
	a_char.print();
	a_char.doSomething(int());

	return 0;
}

 

 

해당 포스트는 '홍정모의 따라하며 배우는 C++' 강의를 수강하며 개인 백업용으로 메모하였습니다.

인프런: https://www.inflearn.com/course/following-c-plus

 

홍정모의 따라하며 배우는 C++ - 인프런

만약 C++를 쉽게 배울 수 있다면 배우지 않을 이유가 있을까요? 성공한 프로그래머로써의 경력을 꿈꾸지만 지금은 당장 하루하루 마음이 초조할 뿐인 입문자 분들을 돕기 위해 친절하고 자세하게 설명해드리는 강의입니다. 초보로 시작하더라도 중급을 넘어 고급 프로그래머로 가는 길목에 들어서고 싶으시다면 최고의 디딤돌이 되어드리겠습니다. 여러분의 꿈을 응원합니다! 초급 프로그래밍 언어 C++ 온라인 강의 C++

www.inflearn.com

반응형
복사했습니다!